mcp-phase-gate 2.0.1 → 2.2.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/README.md CHANGED
@@ -34,7 +34,7 @@ flowchart TD
34
34
 
35
35
  And between one phase and the next there is a **gate**, which is what gives the server its name: ask for a phase while skipping the previous one and the server notices and warns you ([see below](#-the-gate-skipping-a-phase-stops-being-invisible)).
36
36
 
37
- In phase 5 the AI only reads. **You always make the commits and open the PR.**
37
+ In phase 4 the AI commits each batch only with your OK; it never pushes or opens the PR. In phase 5 it only reads.
38
38
 
39
39
  ## Who it's for
40
40
 
@@ -59,7 +59,10 @@ DOCUMENTATION/
59
59
  │ ├── 01 - Análisis Técnico.md ← Phase 1
60
60
  │ ├── 02 - Decisiones.md ← Phase 2
61
61
  │ ├── 03 - Plan Técnico.md ← Phase 3
62
- │ ├── 04 - Ejecución.md ← Phase 4
62
+ │ ├── 04 - Ejecución/ ← Phase 4, with more than one batch: one receipt each
63
+ │ │ ├── L01 - <slug>.md
64
+ │ │ └── L02 - <slug>.md
65
+ │ ├── 04 - Ejecución.md ← Phase 4: the receipt, or the index of receipts
63
66
  │ └── 05 - Auditoría.md ← Phase 5
64
67
  ├── PROJECT-B/
65
68
  │ └── Proyectos/
@@ -69,7 +72,9 @@ DOCUMENTATION/
69
72
  └── <task_name>/
70
73
  ```
71
74
 
72
- The `Proyectos` level is the tasks subfolder, and its name is configurable — see `ORQUESTADOR_TASKS_SUBDIR` below. The document names come from the phase prompts, which is why they are in Spanish by default.
75
+ The `Proyectos` level is the tasks subfolder, and its name is configurable — see `PHASE_GATE_TASKS_SUBDIR` below. The document names come from the phase prompts, which is why they are in Spanish by default.
76
+
77
+ **Batches and receipts.** The phase 3 plan splits the work into batches (`L01`, `L02`…): each one is a change that can be reverted on its own and leaves the application working. Phase 4 runs them one at a time and closes each batch with a short **receipt** (what was done, what was verified, what is waiting for you) and a commit. With a single batch, the receipt is `04 - Ejecución.md` itself and there is no subfolder, unless that batch fails or is discarded: then its receipt goes in `04 - Ejecución/` and the phase isn't marked as closed. With several, the receipts go in `04 - Ejecución/` and `04 - Ejecución.md` is the index written when the last one closes: until then the phase doesn't show as finished. When you ask for phase 4 or 5, the server appends the list of existing receipts to the prompt, so a new chat can pick up without guessing their names.
73
78
 
74
79
  > The server builds this path **automatically**. You (or the LLM) only supply `project`, `task_name` and `file_name`; nobody has to type a full path.
75
80
 
@@ -96,7 +101,7 @@ The code is identical everywhere; the only thing that changes between machines i
96
101
 
97
102
  Nothing to install: `npx` fetches the package the first time and caches it. Everything is configured in your MCP client's file.
98
103
 
99
- **The key you give it is what identifies the server and prefixes its tools**, so you can name it whatever you like. The examples use `mcp-phase-gate`, the same as the npm package and the name the server reports for itself — one name in all three places, so there's nothing to remember.
104
+ **The key you give it is what identifies the server and prefixes its tools and commands**, so you can name it whatever you like. The examples use `mcp-phase-gate`, the same as the npm package and the name the server reports for itself. If you're registering it for the first time, a short key such as `pg` keeps the commands short (`/pg:f1` in Claude Code). **If it's already registered, keep your key**: changing it renames every tool and command, and you'd have to grant their permissions again.
100
105
 
101
106
  Remember that in JSON every `\` must be doubled as `\\`.
102
107
 
@@ -110,8 +115,8 @@ Remember that in JSON every `\` must be doubled as `\\`.
110
115
  "command": "npx",
111
116
  "args": ["-y", "mcp-phase-gate"],
112
117
  "env": {
113
- "ORQUESTADOR_DOCS_PATH": "C:\\Users\\YOUR_USER\\OneDrive - YOUR ORGANISATION\\Documents\\DOCUMENTATION",
114
- "ORQUESTADOR_REPOS_PATH": "C:\\projects"
118
+ "PHASE_GATE_DOCS_PATH": "C:\\Users\\YOUR_USER\\OneDrive - YOUR ORGANISATION\\Documents\\DOCUMENTATION",
119
+ "PHASE_GATE_REPOS_PATH": "C:\\projects"
115
120
  }
116
121
  }
117
122
  }
@@ -132,8 +137,8 @@ Two differences to watch: the top-level key is `servers`, not `mcpServers`, and
132
137
  "command": "npx",
133
138
  "args": ["-y", "mcp-phase-gate"],
134
139
  "env": {
135
- "ORQUESTADOR_DOCS_PATH": "C:\\Users\\YOUR_USER\\OneDrive - YOUR ORGANISATION\\Documents\\DOCUMENTATION",
136
- "ORQUESTADOR_REPOS_PATH": "C:\\projects"
140
+ "PHASE_GATE_DOCS_PATH": "C:\\Users\\YOUR_USER\\OneDrive - YOUR ORGANISATION\\Documents\\DOCUMENTATION",
141
+ "PHASE_GATE_REPOS_PATH": "C:\\projects"
137
142
  }
138
143
  }
139
144
  }
@@ -145,19 +150,21 @@ Two differences to watch: the top-level key is `servers`, not `mcpServers`, and
145
150
  <summary><b>Claude Code</b> — one line</summary>
146
151
 
147
152
  ```bash
148
- claude mcp add mcp-phase-gate --env ORQUESTADOR_DOCS_PATH="C:\Users\YOUR_USER\Documents\DOCUMENTATION" --env ORQUESTADOR_REPOS_PATH="C:\projects" -- npx -y mcp-phase-gate
153
+ claude mcp add mcp-phase-gate --env PHASE_GATE_DOCS_PATH="C:\Users\YOUR_USER\Documents\DOCUMENTATION" --env PHASE_GATE_REPOS_PATH="C:\projects" -- npx -y mcp-phase-gate
149
154
  ```
150
155
 
151
156
  Or by hand, in a `.mcp.json` at the project root, shaped like the Cursor one.
152
157
  </details>
153
158
 
154
- That `ORQUESTADOR_DOCS_PATH` is only an **example** using a OneDrive folder. `G:\My Drive\Documentation`, `D:\Dropbox\Docs` or plain `C:\Docs` with no cloud at all work just as well.
159
+ That `PHASE_GATE_DOCS_PATH` is only an **example** using a OneDrive folder. `G:\My Drive\Documentation`, `D:\Dropbox\Docs` or plain `C:\Docs` with no cloud at all work just as well.
155
160
 
156
161
  > **There is no `.env` on this route.** The package lives inside `node_modules`, which is no place to keep your configuration, so the paths go in the `"env"` block — that's the only option here. The `.env` file only exists on the route below.
157
162
 
158
163
  **Both variables are required and have no default value.** If one is missing, or points at a folder that doesn't exist, the tools that need it say so by name instead of failing later for some unrelated-looking reason.
159
164
 
160
- > Typing `npx mcp-phase-gate` straight into a terminal **looks like it hangs**, and nothing is broken: it's an MCP server waiting for messages on stdin. If you just want to check it's there, run `npx mcp-phase-gate --help`.
165
+ > **Already using it with `ORQUESTADOR_*`?** Those names keep working as aliases of `PHASE_GATE_*`; there's nothing to change. If you define both with different values, `PHASE_GATE_*` wins, and `doctor` and the server log tell you.
166
+
167
+ > Typing `npx mcp-phase-gate` straight into a terminal **looks like it hangs**, and nothing is broken: it's an MCP server waiting for messages on stdin. If you want to check your setup, run `npx mcp-phase-gate doctor` ([see below](#-check-your-setup-doctor)).
161
168
 
162
169
  ---
163
170
 
@@ -183,11 +190,11 @@ cp .env.example .env
183
190
  Single backslashes on Windows, no quotes:
184
191
 
185
192
  ```ini
186
- ORQUESTADOR_DOCS_PATH=C:\Users\YOUR_USER\OneDrive - YOUR ORGANISATION\Documents\DOCUMENTATION
187
- ORQUESTADOR_REPOS_PATH=C:\projects
193
+ PHASE_GATE_DOCS_PATH=C:\Users\YOUR_USER\OneDrive - YOUR ORGANISATION\Documents\DOCUMENTATION
194
+ PHASE_GATE_REPOS_PATH=C:\projects
188
195
  ```
189
196
 
190
- > On Windows, save `.env` as **UTF-8 without BOM**. Creating it from PowerShell with `>` produces UTF-16, accented characters arrive mangled, and the path "doesn't exist" even though it looks right.
197
+ > Save `.env` as **UTF-8** (with or without BOM, either works). Creating it from PowerShell with `>` produces UTF-16: the server detects it, doesn't load it, and tells you so — both in the missing-variable error and in `doctor`.
191
198
 
192
199
  **3. Register the server** as in route A, but swapping `command` and `args` for your clone's `build/index.js`, and dropping the `"env"` block (the paths come from `.env` now):
193
200
 
@@ -204,8 +211,20 @@ In **Claude Code**, a `.mcp.json` at this repo's root can use a **relative** pat
204
211
 
205
212
  ### ✅ Verify it works
206
213
  1. Restart the client, or toggle the server off and on in its MCP panel.
207
- 2. It should show up active with its 8 tools. In **Cursor** that's Settings → MCP; in **VS Code**, the extensions/MCP view; in **Claude Code**, `/mcp`.
208
- 3. Try it from the chat: *"Use the `get_active_task` tool from mcp-phase-gate"* — it should answer, even if only to say there's no active task yet.
214
+ 2. It should show up active with its 9 tools. In **Cursor** that's Settings → MCP; in **VS Code**, the extensions/MCP view; in **Claude Code**, `/mcp`.
215
+ 3. Try it from the chat: *"Use the `get_orchestrator_status` tool from mcp-phase-gate"* — it returns an X-ray of the installation: which variables it sees, which prompts it uses and which projects it finds.
216
+
217
+ ### 🔍 Check your setup: `doctor`
218
+
219
+ From a terminal, with the same variables you give the client:
220
+
221
+ ```bash
222
+ npx mcp-phase-gate doctor
223
+ ```
224
+
225
+ It tells you which version is running and from where (`npx`, global, a dependency or a clone), the value of each variable and where it came from (the environment or `.env`, and whether you used the new name or the alias), which prompt file wins for each phase, which projects exist with their active task, and a list of findings: a missing required variable, a UTF-16 `.env`, a custom `global-rules.md` missing sections the phases cite, a phase with no document… It exits with code `1` if there's any error, so it also works in a script. `doctor --json` gives the same thing as JSON.
226
+
227
+ It's the same information returned by the `get_orchestrator_status` tool and the `phase-gate://estado` resource; from the chat, it also includes which client is connected and which capabilities it announced.
209
228
 
210
229
  ---
211
230
 
@@ -233,23 +252,23 @@ You may want separate documentation sets that never mix — one for work and one
233
252
  ```jsonc
234
253
  {
235
254
  "mcpServers": {
236
- "orquestador-work": {
255
+ "pg-work": {
237
256
  "command": "npx",
238
257
  "args": ["-y", "mcp-phase-gate"],
239
258
  "env": {
240
- "ORQUESTADOR_DOCS_PATH": "C:\\Users\\YOUR_USER\\OneDrive - YOUR ORGANISATION\\Documents\\DOCUMENTATION",
241
- "ORQUESTADOR_REPOS_PATH": "C:\\projects",
242
- "ORQUESTADOR_STATE_PATH": "C:\\Users\\YOUR_USER\\.orquestador\\work"
259
+ "PHASE_GATE_DOCS_PATH": "C:\\Users\\YOUR_USER\\OneDrive - YOUR ORGANISATION\\Documents\\DOCUMENTATION",
260
+ "PHASE_GATE_REPOS_PATH": "C:\\projects",
261
+ "PHASE_GATE_STATE_PATH": "C:\\Users\\YOUR_USER\\.orquestador\\work"
243
262
  }
244
263
  },
245
- "orquestador-personal": {
264
+ "pg-personal": {
246
265
  "command": "npx",
247
266
  "args": ["-y", "mcp-phase-gate"],
248
267
  "env": {
249
- "ORQUESTADOR_DOCS_PATH": "G:\\My Drive\\Documentation",
250
- "ORQUESTADOR_REPOS_PATH": "D:\\dev",
251
- "ORQUESTADOR_TASKS_SUBDIR": "",
252
- "ORQUESTADOR_STATE_PATH": "C:\\Users\\YOUR_USER\\.orquestador\\personal"
268
+ "PHASE_GATE_DOCS_PATH": "G:\\My Drive\\Documentation",
269
+ "PHASE_GATE_REPOS_PATH": "D:\\dev",
270
+ "PHASE_GATE_TASKS_SUBDIR": "",
271
+ "PHASE_GATE_STATE_PATH": "C:\\Users\\YOUR_USER\\.orquestador\\personal"
253
272
  }
254
273
  }
255
274
  }
@@ -258,12 +277,12 @@ You may want separate documentation sets that never mix — one for work and one
258
277
 
259
278
  It's the same package in both — only the paths differ. Each registration can even have its own tasks subfolder; above, the personal one leaves it empty so tasks hang directly off the project.
260
279
 
261
- > ⚠️ **`ORQUESTADOR_STATE_PATH` must differ between registrations.** Share it and the active-task pointers clobber each other **silently**: the state is keyed by project name, so two documentation roots holding a project with the same name end up sharing one entry, and one answers for the other. No error is raised — you just get the wrong task back.
280
+ > ⚠️ **`PHASE_GATE_STATE_PATH` must differ between registrations.** Share it and the active-task pointers clobber each other **silently**: the state is keyed by project name, so two documentation roots holding a project with the same name end up sharing one entry, and one answers for the other. No error is raised — you just get the wrong task back.
262
281
 
263
282
  Two more things that bite and aren't obvious:
264
283
 
265
284
  - **Mirror mode, not streaming.** Google Drive in *streaming* mode and OneDrive with *Files On-Demand* leave files as placeholders until something opens them. This server lives on listing folders and checking whether files exist, so placeholders make it slow, or make it fail with no network. Keep the documentation folder **downloaded locally** if you can.
266
- - **Keep code repos out of the synced folder.** Documentation in the cloud is fine. Code isn't: sync competing with git over `.git/` corrupts repositories. Point `ORQUESTADOR_REPOS_PATH` at a local disk.
285
+ - **Keep code repos out of the synced folder.** Documentation in the cloud is fine. Code isn't: sync competing with git over `.git/` corrupts repositories. Point `PHASE_GATE_REPOS_PATH` at a local disk.
267
286
 
268
287
  ---
269
288
 
@@ -276,7 +295,7 @@ Two more things that bite and aren't obvious:
276
295
  | The compiled server | `build/` in your clone | It's gitignored. **After every `git pull` that touches `src/`, run `npm run build`** or the client keeps executing the previous version. |
277
296
  | This machine's paths | `.env` at the root ([route B](#-route-b--from-source), step 2) | The system user and the documentation folder differ per machine. |
278
297
  | The MCP registration | Your client's config ([route A](#-route-a--with-npx-recommended), or step 3 of [route B](#-route-b--from-source)) | In Cursor and VS Code it holds the absolute path to `build/index.js`, which depends on where you cloned and points into a gitignored folder. No single path can be right on every machine. See [`docs/decisiones.md`](docs/decisiones.md). The exception is Claude Code's `.mcp.json`, which uses a relative path. |
279
- | The `/f1`–`/f5` commands in **Cursor** | `~/.cursor/commands/f<N>.md` | Cursor only reads them from the user folder, so they're copied by hand on each machine. Not an issue in **Claude Code**: they're versioned in `.claude/commands/` and arrive with the pull. |
298
+ | The `/f1`–`/f5` commands as files, **only if your client doesn't show MCP Prompts well** | `~/.cursor/commands/f<N>.md` in Cursor | Since 2.2.0 the server offers them as MCP Prompts and they arrive with every version ([see below](#the-phase-commands)). The file copy is only a fallback; in **Claude Code** inside this repo it's already versioned in `.claude/commands/`. |
280
299
 
281
300
  ### The phase commands
282
301
 
@@ -287,20 +306,24 @@ You invoke them by typing `/f1`, `/f2`, etc. in the chat. **They are orchestrati
287
306
  | `/f1` | `start_task` + `get_phase_prompt(1)` | Phase 1's |
288
307
  | `/f2` | `get_phase_prompt(2)`, reads phase 1's | Phase 2's, after you answer the questions |
289
308
  | `/f3` | `get_phase_prompt(3)`, reads the previous ones | Phase 3's |
290
- | `/f4` | `get_phase_prompt(4)`, reads the plan | Phase 4's, short and at the end |
309
+ | `/f4` | `get_phase_prompt(4)`, reads the plan | One receipt per batch; the phase document when it closes |
291
310
  | `/f5` | `get_phase_prompt(5)` | Phase 5's: the audit and the PR description, which also go in the chat |
292
311
 
293
312
  No command names a file: the name is declared in each phase prompt's header and `get_phase_prompt` hands it to the model.
294
313
 
295
- `/f1` and `/f5` expect data after the command: `/f1` needs project, task_name and the full requirement; `/f5`, which commits to audit. The other four start from `get_active_task`.
314
+ `/f1` needs project, task_name and the full requirement. `/f5` accepts a commit range; without one it audits the commits recorded in the phase 4 receipts, and only asks if there are no receipts. The rest start from `get_active_task`.
296
315
 
297
- **Where to put them, per client:**
316
+ **They arrive on their own, as MCP Prompts.** Since 2.2.0 the server offers each command as an MCP prompt (`f1`…`f5`), with its text in [`prompts/comandos/`](prompts/comandos). Nothing to copy into any folder, and they update with every package version. All their arguments are optional — `project`; in `/f1` also `task_name` and `contexto`; in `/f5`, `commits` —: whatever you don't pass, the command asks for, and a long context can be pasted after the command as plain chat text. A phase you add yourself (a `fase-6`) gets its own `/f6` with a generic text, and a `comandos/fN.md` in your own prompts folder wins over the packaged one.
298
317
 
299
- - **Claude Code:** already in [`.claude/commands/`](.claude/commands), versioned. They work when you open this repo, no setup. To use them from your other projects, copy them to `~/.claude/commands/`.
300
- - **Cursor:** copy them to `~/.cursor/commands/`. From there they work across all your projects.
301
- - **VS Code:** no slash commands of its own; paste the file contents into the chat.
318
+ How they look in each client:
302
319
 
303
- The exact contents of all five are also in [`docs/comandos-de-fase.md`](docs/comandos-de-fase.md) (Spanish).
320
+ | Client | `/f1`…`/f5` visible | Arguments with spaces | `@` resources | Direct confirmations | Notes |
321
+ |--------|---------------------|-----------------------|---------------|----------------------|-------|
322
+ | Claude Code | To be verified (per its docs: `/<key>:f1 (MCP)`, with `/mcp__<key>__f1` as an alias) | To be verified | To be verified | To be verified | |
323
+ | Cursor | To be verified (per its docs: in the MCP prompts menu) | To be verified | To be verified | To be verified | On Windows, `PHASE_GATE_ELICITATION=off` while the form bug stays open ([see below](#-protected-documents)) |
324
+ | VS Code | To be verified (its docs contradict themselves between `/<key>.f1` and `mcp.<key>.f1`) | To be verified | To be verified | To be verified | |
325
+
326
+ **If your client doesn't show them comfortably**, use the files: in this repo, Claude Code already reads them from [`.claude/commands/`](.claude/commands) (identical to `prompts/comandos/`); to use them in your other projects, copy them to `~/.claude/commands/`, or to `~/.cursor/commands/` in Cursor. More detail in [`docs/comandos-de-fase.md`](docs/comandos-de-fase.md) (Spanish).
304
327
 
305
328
  ---
306
329
 
@@ -327,24 +350,64 @@ Three things it deliberately does **not** do:
327
350
  - **It doesn't make you type anything extra.** You still type `/f4` and nothing else. Working out which task you mean is the server's job, using the pointer it already keeps — not the model asking you.
328
351
  - **It doesn't go quiet when it can't check.** If several projects have an active task and none was named, it says so instead of pretending everything is in order.
329
352
 
353
+ **It also flags what the previous phase left open.** If the previous phase's document has a `## Validaciones pendientes` section with something in it, or if, when asking for phase 5, some batch receipt ended up `con pendientes`, `fallido` or `descartado`, the prompt also arrives with a `## PENDIENTES HEREDADOS (detectados por el servidor)` block naming them, so the AI tells you before starting. A section that only says "Ninguna." doesn't count. It doesn't block anything either: it only points at what the prompt already required writing.
354
+
355
+ ---
356
+
357
+ ## 🔒 Protected documents
358
+
359
+ Each phase works on top of the previous phases' documents, so rewriting one that's already been passed silently changes the basis of everything that came after. That's why `write_central_doc` refuses, **without `force: true`**, to write:
360
+
361
+ - **The initial context** (`00 - Contexto Inicial.md`), always, even to create it. Only `start_task` writes it without `force`.
362
+ - **The document of a frozen phase.** A phase freezes as soon as a later phase's document exists, or as soon as phase 4 has any receipt in `04 - Ejecución/`. Receipts count as phase 4: they and `04 - Ejecución.md` are frozen only by phase 5.
363
+
364
+ Creating a phase document that doesn't exist yet is never refused; a new receipt is, once phase 5 is present. Names are compared ignoring case and in the same Unicode form, so spelling them differently doesn't get around the protection.
365
+
366
+ **If a legitimate fix is needed** — a typo in the analysis when the decisions are already written — it depends on your client:
367
+
368
+ - **If your client supports direct confirmations** (MCP elicitation), the server **asks you**, in a form in your client with one checkbox: "Reescribir `01 - Análisis Técnico.md`". Tick it and accept, and it's written, with the response saying it was with your confirmation; say no or close the form, and nothing is written. Here `force` isn't enough: the AI sets it, and that's exactly what the question avoids.
369
+ - **If it doesn't**, it works as before: the refusal tells the AI it needs your explicit confirmation in the chat and `force: true`, and the response warns that the document was overwritten (or created, if it didn't exist).
370
+
371
+ The same goes for **starting a new project** with `crear_proyecto: true`: with direct confirmations, the server asks you before creating the folder.
372
+
373
+ If your client announces direct confirmations but the form never shows up, the server waits three minutes and writes nothing; the second time in a row it doesn't write either (even if the AI set `force`) and stops asking for the rest of the session: from then on `force` decides again, with your confirmation in the chat. If your client cancels the call before you answer, nothing is written. To avoid this from the start, register the server with `PHASE_GATE_ELICITATION=off`. **On Cursor for Windows, `off` is recommended** while the bug where the form doesn't appear stays open.
374
+
375
+ On top of that, `write_central_doc` only writes **inside the folder of a task that already exists**: it doesn't create stray folders in the documentation.
376
+
330
377
  ---
331
378
 
332
379
  ## 🛠 Available tools
333
380
 
334
381
  | Tool | What it does |
335
382
  |------|--------------|
336
- | `start_task` | Creates the folder for a **new** task (`<PROJECT>/<tasks subfolder>/<task>`), saves the initial context and makes it the **active task of its project**. If the task already exists it refuses and points you at `switch_task`, so the initial context is never overwritten. |
383
+ | `start_task` | Creates the folder for a **new** task (`<PROJECT>/<tasks subfolder>/<task>`), saves the initial context and makes it the **active task of its project**. If the task already exists it refuses and points you at `switch_task`, so the initial context is never overwritten. Starting a new project takes `crear_proyecto: true` and, if your client can, your direct confirmation. |
337
384
  | `get_active_task` | Returns the active task and **which phase it's on**, derived from the documents it already has. Without `project` it answers for every project. Use it when opening a new chat or after losing track. |
338
385
  | `list_tasks` | Lists the tasks that exist and each one's phase, most recently touched first. For picking up something old or seeing what was left half-done. |
339
386
  | `switch_task` | Changes which task is active for a project. **Touches no files**: it's the correct way to resume an existing task. |
340
- | `get_phase_prompt` | Returns the behaviour rules for a phase (1–5), with the global rules prepended and the exact document filenames appended. The text lives in `prompts/`. It also checks the **gate**: pass `project` and, if you skipped a phase, the prompt arrives with the warning on top. |
387
+ | `get_phase_prompt` | Returns the behaviour rules for a phase (1–5), with the global rules prepended and the exact document filenames appended. The text lives in `prompts/`. It also checks the **gate**: pass `project` and, if you skipped a phase, the prompt arrives with the warning on top, followed by the inherited pending items if there are any. |
341
388
  | `read_central_doc` | Reads a file from the central documentation. **Recommended:** pass `project` + `task_name` + `file_name` and let the server build the path. It also accepts a relative `file_path`. |
342
- | `write_central_doc` | Writes/overwrites a file. Same parameters as `read_central_doc` plus `content`. |
389
+ | `write_central_doc` | Writes/overwrites a file inside a task folder. Same parameters as `read_central_doc` plus `content`, and `force` for [protected documents](#-protected-documents) when your client doesn't support direct confirmations. |
343
390
  | `read_cross_repo` | Reads files from your other local repos, so you can check how another codebase solved something without switching windows. |
391
+ | `get_orchestrator_status` | The installation X-ray, same as [`doctor`](#-check-your-setup-doctor): package version and path, variables and where each came from, which prompt wins per phase, projects with their active task, and findings. Plus the connected client and its capabilities. Use it when something doesn't behave as you expect. |
392
+
393
+ They all declare their MCP annotations (read-only, destructive, idempotent), so a cautious client can let the read-only ones through without asking. Only `write_central_doc` is destructive.
394
+
395
+ ### `phase-gate://` resources
396
+
397
+ The server also exposes the documents as **read-only resources**, which compatible clients show when you type `@` in the chat, to attach them without asking the AI:
398
+
399
+ | Resource | What it is |
400
+ |----------|------------|
401
+ | `phase-gate://estado` | The `doctor` X-ray, in Markdown |
402
+ | `phase-gate://fases` | The available phases, with their document, receipts, and which prompt file wins |
403
+ | `phase-gate://tareas/{proyecto}/{tarea}/{documento}` | A task document |
404
+ | `phase-gate://tareas/{proyecto}/{tarea}/recibos/{recibo}` | A phase 4 batch receipt |
405
+
406
+ The list only includes the existing documents of **each project's active task**, so the picker doesn't drown; the rest is reachable through the template, which autocompletes project, task and document. They have the same defences as `read_central_doc`: they can't leave the task folder, don't expose dot-files, and don't read more than 1 MB.
344
407
 
345
408
  ### How the server builds paths
346
- - **Documentation:** `ORQUESTADOR_DOCS_PATH` / `<PROJECT>` / `ORQUESTADOR_TASKS_SUBDIR` / `<task>` / `<file>`
347
- - **Code repos:** `ORQUESTADOR_REPOS_PATH` / `<repo>` / `<file>`
409
+ - **Documentation:** `PHASE_GATE_DOCS_PATH` / `<PROJECT>` / `PHASE_GATE_TASKS_SUBDIR` / `<task>` / `<file>`
410
+ - **Code repos:** `PHASE_GATE_REPOS_PATH` / `<repo>` / `<file>`
348
411
 
349
412
  ### What counts as a project
350
413
 
@@ -352,7 +415,7 @@ There is no list of projects to maintain. **A project is any folder that contain
352
415
 
353
416
  A project that doesn't exist is rejected, showing the list of the ones that do, instead of silently creating a stray folder. To **start** a brand new project you have to ask on purpose: `start_task` accepts `crear_proyecto: true`, and without that flag a typo can't pollute your documentation.
354
417
 
355
- > If you leave `ORQUESTADOR_TASKS_SUBDIR` empty, the rule can't be applied and **any** folder counts as a project.
418
+ > If you leave `PHASE_GATE_TASKS_SUBDIR` empty, the rule can't be applied and **any** folder counts as a project.
356
419
 
357
420
  ---
358
421
 
@@ -367,7 +430,10 @@ prompts/
367
430
  ├── fase-2-decisiones.md
368
431
  ├── fase-3-plan-tecnico.md
369
432
  ├── fase-4-ejecucion.md
370
- └── fase-5-auditoria-pr.md ← includes the PR description contract
433
+ ├── fase-5-auditoria-pr.md ← includes the PR description contract
434
+ └── comandos/ ← the /f1…/f5 text, served by the server as MCP Prompts
435
+ ├── f1.md
436
+ └── …
371
437
  ```
372
438
 
373
439
  **They are read on every call.** Edit a `.md` and the next `get_phase_prompt` already returns the new version — no `npm run build`, no client restart. That's what makes it practical to tune a phase and test it immediately.
@@ -395,7 +461,7 @@ Format rules:
395
461
 
396
462
  ### Using your own prompts without losing the packaged ones
397
463
 
398
- `ORQUESTADOR_PROMPTS_PATH` points at a folder of yours that is **layered on top of** the packaged one. It does not replace it.
464
+ `PHASE_GATE_PROMPTS_PATH` points at a folder of yours that is **layered on top of** the packaged one. It does not replace it.
399
465
 
400
466
  To change only phase 2, your folder holds **one file**:
401
467
 
@@ -411,6 +477,7 @@ Three details that matter:
411
477
  - **Layering is by phase number, not by filename.** Your `fase-2-my-way.md` overrides the package's `fase-2-decisiones.md`. You don't have to guess the original's name.
412
478
  - **It also adds.** A `fase-6-deployment.md` in your folder creates phase 6, nothing else needed.
413
479
  - **Your `global-rules.md` replaces the packaged one, it doesn't stack.** Concatenating two rule sets makes it impossible to tell which file an unwanted instruction came from, exactly when you need to know. If you want the packaged rules plus extras, copy them and add.
480
+ **Watch out:** the packaged rules include the `EVIDENCIA`, `BASE DE CADA DECISIÓN` and `PENDIENTES HEREDADOS` sections, which the phases refer to. A `global-rules.md` of your own without them leaves the phases pointing at rules that don't exist; copy them into your file.
414
481
 
415
482
  On the `npx` route this is the **only** way to customise: the packaged prompts live inside `node_modules`, where editing them achieves nothing because the next install wipes them.
416
483
 
@@ -437,13 +504,16 @@ Loaded from the `.env` at the repo root, or from the `"env"` block in your clien
437
504
 
438
505
  The first two are **required and have no default**: they point at folders that only exist on your machine, so any default would be somebody else's path. If one is missing, or points at something that doesn't exist or isn't a folder, the tools that use it return the variable name and where to configure it. Tools that don't depend on it — like `get_phase_prompt` — keep working.
439
506
 
507
+ Each variable also has an **alias** with the old name, `ORQUESTADOR_*` (e.g. `ORQUESTADOR_DOCS_PATH`), which keeps working so no existing setup breaks. If you define both with different values, `PHASE_GATE_*` wins, and `doctor` and the server log tell you.
508
+
440
509
  | Variable | Default | Description |
441
510
  |----------|---------|-------------|
442
- | `ORQUESTADOR_DOCS_PATH` | **none, required** | Path to the root of your central documentation. **Different on each machine.** |
443
- | `ORQUESTADOR_REPOS_PATH` | **none, required** | Path to the folder holding your code repositories. |
444
- | `ORQUESTADOR_TASKS_SUBDIR` | `Proyectos` | Subfolder where tasks live inside each project. Empty means tasks hang directly off the project. It's also the rule that decides what counts as a project (see above). |
445
- | `ORQUESTADOR_PROMPTS_PATH` | none | Folder holding your own prompts. They are **layered on top of** the packaged ones rather than replacing them, so you only drop in the phases you want to change ([see above](#using-your-own-prompts-without-losing-the-packaged-ones)). |
446
- | `ORQUESTADOR_STATE_PATH` | Your user data folder (see above) | Where the active-task pointers are stored. Rarely needs touching; useful to isolate state in tests, and **required if you register the server more than once** (see [More than one documentation folder?](#-more-than-one-documentation-folder-optional)). |
511
+ | `PHASE_GATE_DOCS_PATH` | **none, required** | Path to the root of your central documentation. **Different on each machine.** |
512
+ | `PHASE_GATE_REPOS_PATH` | **none, required** | Path to the folder holding your code repositories. |
513
+ | `PHASE_GATE_TASKS_SUBDIR` | `Proyectos` | Subfolder where tasks live inside each project. Empty means tasks hang directly off the project. It's also the rule that decides what counts as a project (see above). |
514
+ | `PHASE_GATE_PROMPTS_PATH` | none | Folder holding your own prompts. They are **layered on top of** the packaged ones rather than replacing them, so you only drop in the phases you want to change ([see above](#using-your-own-prompts-without-losing-the-packaged-ones)). |
515
+ | `PHASE_GATE_STATE_PATH` | Your user data folder (see above) | Where the active-task pointers are stored. Rarely needs touching; useful to isolate state in tests, and **required if you register the server more than once** (see [More than one documentation folder?](#-more-than-one-documentation-folder-optional)). |
516
+ | `PHASE_GATE_ELICITATION` | none (enabled) | `off` (or `false`, `0`, `no`) so the server doesn't ask the client for [direct confirmations](#-protected-documents) and lets `force` decide, as before. Recommended on Cursor for Windows. No alias. |
447
517
 
448
518
  ---
449
519
 
@@ -455,11 +525,19 @@ The source lives in `src/`, split by responsibility:
455
525
 
456
526
  | File | What it handles |
457
527
  |------|-----------------|
458
- | `config.ts` | Environment paths, where state lives, server identity |
528
+ | `config.ts` | Environment variables and their aliases, reading `.env`, where state lives, server identity |
459
529
  | `paths.ts` | How every path is built, and the check that it stays inside its base |
460
530
  | `phases.ts` | Discovering the phases and assembling each one's prompt |
461
- | `tasks.ts` | Which tasks exist, which phase each is on, and which one is active |
462
- | `index.ts` | The server's tools. It computes nothing: it translates between the protocol and the above |
531
+ | `tasks.ts` | Which tasks exist, which phase each is on, which one is active, the gate and inherited pending items |
532
+ | `schemas.ts` | Each tool's arguments, with their description and error message |
533
+ | `servidor.ts` | The server's tools. It computes nothing: it translates between the protocol and the above |
534
+ | `recursos.ts` | The `phase-gate://` resources |
535
+ | `prompts-mcp.ts` | The `/f1`…`/fN` commands as MCP Prompts |
536
+ | `confirmacion.ts` | Direct confirmations to the user (elicitation) |
537
+ | `diagnostico.ts` | The X-ray shared by `doctor`, `get_orchestrator_status` and `phase-gate://estado` |
538
+ | `cli.ts` | The command line: `doctor`, `--help`, `--version` |
539
+ | `log.ts` | Logging: stderr and, with a client connected, MCP notifications |
540
+ | `index.ts` | The entry point: the command line, or the server over stdio |
463
541
 
464
542
  The client runs the compiled `build/index.js`, so after any change:
465
543
 
@@ -485,7 +563,7 @@ yours. CI runs them on every PR.
485
563
  |-------------------------|-------|
486
564
  | What the project does today | This README (and its Spanish twin, [`README.es.md`](README.es.md) — **change one, change the other**) |
487
565
  | Why it's designed this way | [`docs/decisiones.md`](docs/decisiones.md) (Spanish) |
488
- | The `/f1`–`/f5` commands, to recreate them elsewhere | [`docs/comandos-de-fase.md`](docs/comandos-de-fase.md) (Spanish) |
566
+ | The `/f1`–`/f5` commands | Their text in [`prompts/comandos/`](prompts/comandos); how they reach each client in [`docs/comandos-de-fase.md`](docs/comandos-de-fase.md) (Spanish) |
489
567
  | That a change broke nothing | [`test/`](test), via `npm test`. CI runs them on every PR |
490
568
  | What's still missing | [Issues](https://github.com/Montse2308/mcp-phase-gate/issues) — grouped under the `v1-uso-propio` and `v2-publico` labels |
491
569
  | What's already been done | The commit history and merged PRs |
package/build/cli.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare const AYUDA: string;
2
+ export declare const AVISO_TERMINAL = "Esperando un cliente MCP por stdin. Si quer\u00EDas revisar la instalaci\u00F3n: `npx mcp-phase-gate doctor`.";
3
+ export type Salida = {
4
+ out: (texto: string) => void;
5
+ err: (texto: string) => void;
6
+ };
7
+ export type ResultadoCli = {
8
+ accion: "servidor";
9
+ } | {
10
+ accion: "salir";
11
+ codigo: number;
12
+ };
13
+ export declare function ejecutarCli(argv: string[], salida?: Salida, esTerminal?: boolean): ResultadoCli;
14
+ //# sourceMappingURL=cli.d.ts.map
package/build/cli.js ADDED
@@ -0,0 +1,98 @@
1
+ // La línea de comandos del paquete. Sin argumentos, el binario es el servidor MCP que lanza el
2
+ // cliente; con argumentos, es una herramienta para la persona que lo instaló.
3
+ //
4
+ // Un servidor MCP habla JSON-RPC por stdin y no imprime nada por stdout. Ejecutado a mano en
5
+ // una terminal —que es exactamente lo que hace quien acaba de leer `npx mcp-phase-gate` en el
6
+ // README— se queda esperando entrada, sin señal de vida, y parece roto. Por eso existen la ayuda
7
+ // y `doctor`: el primero explica qué es esto, el segundo dice si está bien instalado.
8
+ import { parseArgs } from "util";
9
+ import { SERVER_NAME, SERVER_VERSION } from "./config.js";
10
+ import { diagnosticar, formatearDiagnostico } from "./diagnostico.js";
11
+ export const AYUDA = `${SERVER_NAME} v${SERVER_VERSION}
12
+
13
+ Servidor MCP que parte cada requerimiento en cinco fases con compuerta y obliga a que cada
14
+ una deje su documento en una documentación central.
15
+
16
+ El servidor NO se ejecuta a mano: lo lanza tu cliente MCP. Ejecutado directamente se queda
17
+ esperando mensajes JSON-RPC por stdin, que es lo que parece que se colgó.
18
+
19
+ Uso:
20
+ ${SERVER_NAME} Arranca el servidor MCP por stdio (lo hace tu cliente).
21
+ ${SERVER_NAME} doctor Revisa la instalación: variables, prompts, proyectos y avisos.
22
+ ${SERVER_NAME} doctor --json Lo mismo, como JSON.
23
+ ${SERVER_NAME} --version La versión.
24
+ ${SERVER_NAME} --help Esta ayuda.
25
+
26
+ Regístralo en tu cliente (Cursor, VS Code, Claude Code) así:
27
+
28
+ {
29
+ "mcpServers": {
30
+ "${SERVER_NAME}": {
31
+ "command": "npx",
32
+ "args": ["-y", "mcp-phase-gate"],
33
+ "env": {
34
+ "PHASE_GATE_DOCS_PATH": "<tu carpeta de documentación>",
35
+ "PHASE_GATE_REPOS_PATH": "<tu carpeta de repositorios>"
36
+ }
37
+ }
38
+ }
39
+ }
40
+
41
+ Variables de entorno (los nombres ORQUESTADOR_* de antes siguen valiendo como alias):
42
+ PHASE_GATE_DOCS_PATH Obligatoria. Raíz de la Documentación Central.
43
+ PHASE_GATE_REPOS_PATH Obligatoria. Carpeta con tus repositorios de código.
44
+ PHASE_GATE_TASKS_SUBDIR Subcarpeta de tareas dentro de cada proyecto. Por defecto "Proyectos".
45
+ PHASE_GATE_PROMPTS_PATH Carpeta de prompts propios, que se superponen a los del paquete.
46
+ PHASE_GATE_STATE_PATH Dónde guardar los punteros de tarea activa.
47
+ PHASE_GATE_ELICITATION "off" para que el servidor no pida confirmaciones al cliente.
48
+
49
+ Documentación: https://github.com/Montse2308/mcp-phase-gate`;
50
+ export const AVISO_TERMINAL = "Esperando un cliente MCP por stdin. Si querías revisar la instalación: `npx mcp-phase-gate doctor`.";
51
+ const SALIDA_DE_PROCESO = {
52
+ out: (texto) => process.stdout.write(`${texto}\n`),
53
+ err: (texto) => process.stderr.write(`${texto}\n`),
54
+ };
55
+ function invalido(salida) {
56
+ salida.err(AYUDA);
57
+ return { accion: "salir", codigo: 2 };
58
+ }
59
+ export function ejecutarCli(argv, salida = SALIDA_DE_PROCESO, esTerminal = Boolean(process.stdin.isTTY)) {
60
+ if (argv.length === 0) {
61
+ // stderr, no stdout: stdout es el canal del JSON-RPC aunque nadie esté escuchando.
62
+ if (esTerminal)
63
+ salida.err(AVISO_TERMINAL);
64
+ return { accion: "servidor" };
65
+ }
66
+ let valores;
67
+ let posicionales;
68
+ try {
69
+ ({ values: valores, positionals: posicionales } = parseArgs({
70
+ args: argv,
71
+ allowPositionals: true,
72
+ strict: true,
73
+ options: {
74
+ help: { type: "boolean", short: "h" },
75
+ version: { type: "boolean", short: "v" },
76
+ json: { type: "boolean" },
77
+ },
78
+ }));
79
+ }
80
+ catch {
81
+ return invalido(salida);
82
+ }
83
+ if (valores.help) {
84
+ salida.out(AYUDA);
85
+ return { accion: "salir", codigo: 0 };
86
+ }
87
+ if (valores.version) {
88
+ salida.out(SERVER_VERSION);
89
+ return { accion: "salir", codigo: 0 };
90
+ }
91
+ if (posicionales.length === 1 && posicionales[0] === "doctor") {
92
+ const diagnostico = diagnosticar();
93
+ salida.out(valores.json ? JSON.stringify(diagnostico, null, 2) : formatearDiagnostico(diagnostico, "texto"));
94
+ return { accion: "salir", codigo: diagnostico.hallazgos.some((h) => h.severidad === "error") ? 1 : 0 };
95
+ }
96
+ return invalido(salida);
97
+ }
98
+ //# sourceMappingURL=cli.js.map
package/build/config.d.ts CHANGED
@@ -1,11 +1,35 @@
1
1
  export declare const MODULE_DIR: string;
2
+ export declare function envPorDefecto(): string;
3
+ export declare function clavesDeEnv(): ReadonlySet<string>;
4
+ export declare function errorDeEnv(): string | null;
5
+ export declare function reiniciarEnv(): void;
6
+ export declare function cargarEnv(archivo?: string): void;
7
+ export type NombreLogico = "DOCS_PATH" | "REPOS_PATH" | "TASKS_SUBDIR" | "PROMPTS_PATH" | "STATE_PATH" | "ELICITATION";
8
+ type Nombres = {
9
+ principal: string;
10
+ alias: string | null;
11
+ esRuta: boolean;
12
+ };
13
+ export declare const VARIABLES: Record<NombreLogico, Nombres>;
14
+ export declare const NOMBRES_LOGICOS: NombreLogico[];
15
+ export type FuenteVariable = "entorno" | ".env" | "sin definir";
16
+ export type LecturaVariable = {
17
+ logica: NombreLogico;
18
+ valor: string | undefined;
19
+ usada: string | null;
20
+ fuente: FuenteVariable;
21
+ discrepancia: string | null;
22
+ };
23
+ export declare function leerVariable(logica: NombreLogico): LecturaVariable;
24
+ export declare function nombrarVariable(logica: NombreLogico): string;
2
25
  export type RutaRequerida = {
3
- variable: string;
26
+ logica: NombreLogico;
4
27
  apuntaA: string;
5
28
  };
6
29
  export declare const DOCS: RutaRequerida;
7
30
  export declare const REPOS: RutaRequerida;
8
- export declare function requireBasePath({ variable, apuntaA }: RutaRequerida): string;
31
+ export declare function requireBasePath({ logica, apuntaA }: RutaRequerida): string;
32
+ export declare const TASKS_SUBDIR_POR_DEFECTO = "Proyectos";
9
33
  export declare function tasksSubdir(): string;
10
34
  export declare function defaultPromptsPath(): string;
11
35
  export declare function promptsLayers(): string[];
@@ -14,4 +38,5 @@ export declare function stateDir(): string;
14
38
  export declare function stateFile(): string;
15
39
  export declare const SERVER_NAME = "mcp-phase-gate";
16
40
  export declare const SERVER_VERSION: string;
41
+ export {};
17
42
  //# sourceMappingURL=config.d.ts.map