orquestra-mcp 1.1.0 → 1.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orquestra-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "Servidor MCP de Orquestra -- 35 tools para leer y escribir tareas, ideas, changelog, infraestructura y más desde un agente de IA",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { readConfigFile, CONFIG_PATH } from './configFile.mjs'
1
+ import { readConfigFile, resolveProjectForCwd, CONFIG_PATH } from './configFile.mjs'
2
2
 
3
3
  const DEFAULT_API_BASE = 'https://api.orquestra.me'
4
4
 
@@ -7,7 +7,16 @@ export function loadConfig() {
7
7
  const apiBase = process.env.ORQUESTRA_API_BASE || fileConfig?.apiBase || DEFAULT_API_BASE
8
8
  const apiToken = process.env.ORQUESTRA_API_TOKEN || fileConfig?.apiToken
9
9
  const workspaceId = process.env.ORQUESTRA_WORKSPACE_ID || fileConfig?.workspaceId
10
- const defaultProjectId = process.env.ORQUESTRA_DEFAULT_PROJECT_ID || fileConfig?.defaultProjectId
10
+
11
+ // ORQUESTRA_DEFAULT_PROJECT_ID (env var, ej. desde un MCP config con
12
+ // scope local/project de Claude Code) siempre gana -- es una anulación
13
+ // explícita. Si no hay env var, se busca en el mapa carpeta->proyecto del
14
+ // config file, subiendo desde cwd (ver resolveProjectForCwd). Si ninguna
15
+ // carpeta ancestro está mapeada, defaultProjectId queda undefined a
16
+ // propósito -- resolveProjectId() en server.mjs debe fallar explícito en
17
+ // vez de usar el proyecto de otra carpeta.
18
+ const defaultProjectId = process.env.ORQUESTRA_DEFAULT_PROJECT_ID
19
+ || resolveProjectForCwd(fileConfig?.projects, process.cwd())
11
20
 
12
21
  const missing = []
13
22
  if (!apiToken) missing.push('ORQUESTRA_API_TOKEN')
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
2
2
  import { homedir } from 'os'
3
- import { join, dirname } from 'path'
3
+ import { join, dirname, sep } from 'path'
4
4
 
5
5
  export const CONFIG_PATH = join(homedir(), '.orquestra', 'mcp-config.json')
6
6
 
@@ -17,3 +17,29 @@ export function writeConfigFile(data) {
17
17
  mkdirSync(dirname(CONFIG_PATH), { recursive: true })
18
18
  writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2), { mode: 0o600 })
19
19
  }
20
+
21
+ // Guarda/actualiza el projectId de UNA carpeta sin pisar las demás -- setup
22
+ // se puede correr varias veces, una por cada carpeta contenedora de repos
23
+ // (ver resolveProjectForCwd), y cada corrida solo debe tocar su propia
24
+ // entrada.
25
+ export function upsertProjectMapping({ apiBase, apiToken, workspaceId, folderPath, projectId }) {
26
+ const existing = readConfigFile() || {}
27
+ const projects = { ...(existing.projects || {}) }
28
+ projects[folderPath] = projectId
29
+ writeConfigFile({ apiBase, apiToken, workspaceId, projects })
30
+ }
31
+
32
+ // Busca, subiendo desde cwd hacia la raíz, la carpeta mapeada más específica
33
+ // -- mismo criterio que .editorconfig/.gitconfig: el ancestro más cercano
34
+ // gana, así que un repo individual con su propio mapeo puede sobreescribir
35
+ // el de la carpeta contenedora que lo engloba.
36
+ export function resolveProjectForCwd(projects, cwd) {
37
+ if (!projects) return undefined
38
+ let best = null
39
+ for (const folderPath of Object.keys(projects)) {
40
+ const normalized = folderPath.endsWith(sep) ? folderPath.slice(0, -1) : folderPath
41
+ if (cwd !== normalized && !cwd.startsWith(normalized + sep)) continue
42
+ if (!best || normalized.length > best.length) best = normalized
43
+ }
44
+ return best ? projects[best] : undefined
45
+ }
package/src/server.mjs CHANGED
@@ -49,7 +49,9 @@ const SERVER_INSTRUCTIONS = `Este server conecta con Orquestra, la app de gesti
49
49
 
50
50
  Al empezar una sesión de trabajo en este proyecto, llama una vez a get_project_context para saber en qué fase está, qué tareas hay abiertas/vencidas/bloqueadas y qué se hizo recientemente. Úsalo como contexto de fondo, no lo repitas en cada mensaje.
51
51
 
52
- Si get_project_context devuelve un proyecto vacío (sin módulos ni tareas), es la primera vez que se usa este MCP aquí -- antes de crear nada a mano, llama a get_setup_guide y ofrece al usuario aplicar un playbook de etapa (get_stage_playbook + apply_stage_playbook) o iniciar un proyecto nuevo (init_project).
52
+ Si get_project_context devuelve un proyecto vacío (sin módulos ni tareas), es la primera vez que se usa este MCP aquí -- pero "vacío en Orquestra" NO significa "recién empezado en la realidad". Antes de ofrecer nada, pregúntale al usuario: ¿este proyecto es una idea nueva, o ya existe y solo lo estás conectando por primera vez? Según la respuesta:
53
+ - Si es una idea nueva: llama a get_setup_guide y ofrece un playbook de etapa "pre" (get_stage_playbook + apply_stage_playbook).
54
+ - Si ya existe y está avanzado: usa update_project para poner la fase real (mvp/v1/beta/producción, no "idea"), crea una distribution por cada plataforma real que te mencione, y ofrece el playbook de etapa "build" en vez de "pre" -- no le apliques la plantilla de idea/diseño a un proyecto que ya está en desarrollo activo.
53
55
 
54
56
  Durante el trabajo normal de código, sin que el usuario te lo pida explícitamente:
55
57
  - Si te pide una funcionalidad o arreglo real (no una pregunta rápida ni exploración de código sin cambios), revisa con list_tasks/search si ya existe una tarea relacionada; si no, créala con create_task antes de empezar a trabajar.
@@ -66,7 +68,14 @@ export function createServer() {
66
68
 
67
69
  function resolveProjectId(projectId) {
68
70
  if (config.defaultProjectId) return config.defaultProjectId
69
- if (!projectId) throw new Error('Falta projectId -- no hay proyecto default configurado, corre "npx orquestra-mcp setup" o pasa projectId explícito.')
71
+ if (!projectId) {
72
+ throw new Error(
73
+ `No hay ningún proyecto de Orquestra configurado para esta carpeta (${process.cwd()}). ` +
74
+ `No asumas que se refiere a otro proyecto que hayas visto en esta cuenta -- dile al usuario que ` +
75
+ `corra "npx orquestra-mcp setup" en esta carpeta (o en la carpeta contenedora, si este proyecto ` +
76
+ `vive repartido en varios repos) para conectarla.`
77
+ )
78
+ }
70
79
  return projectId
71
80
  }
72
81
 
@@ -531,6 +540,29 @@ export function createServer() {
531
540
  }
532
541
  )
533
542
 
543
+ server.registerTool(
544
+ 'update_project',
545
+ {
546
+ description: 'Edita el proyecto configurado (name, description, phase, projectType, url). Útil para sincronizar la fase real de un proyecto que ya estaba avanzado antes de conectarlo a Orquestra -- get_project_context puede verse "vacío" (sin módulos/tareas) sin que eso signifique que el proyecto recién empieza.',
547
+ inputSchema: {
548
+ projectId: z.string().optional().describe(PROJECT_ID_DESC),
549
+ name: z.string().optional(),
550
+ description: z.string().optional(),
551
+ phase: z.string().optional().describe('idea | mvp | v1 | v2 | beta | produccion'),
552
+ projectType: z.enum(['code', 'content', 'hybrid']).optional(),
553
+ url: z.string().optional(),
554
+ },
555
+ },
556
+ async ({ projectId, ...body }) => {
557
+ try {
558
+ const data = await apiPatch(config, '/v1/projects', { id: resolveProjectId(projectId), ...body })
559
+ return textResult(data)
560
+ } catch (err) {
561
+ return errorResult(err)
562
+ }
563
+ }
564
+ )
565
+
534
566
  server.registerTool(
535
567
  'apply_stage_playbook',
536
568
  {
package/src/setup.mjs CHANGED
@@ -2,7 +2,9 @@ import { createServer } from 'http'
2
2
  import { randomBytes } from 'crypto'
3
3
  import { exec } from 'child_process'
4
4
  import { createInterface } from 'readline'
5
- import { writeConfigFile, CONFIG_PATH } from './configFile.mjs'
5
+ import { homedir } from 'os'
6
+ import { resolve } from 'path'
7
+ import { upsertProjectMapping, CONFIG_PATH } from './configFile.mjs'
6
8
 
7
9
  const DEFAULT_API_BASE = 'https://api.orquestra.me'
8
10
  const DEFAULT_WEB_URL = 'https://app.orquestra.me'
@@ -114,8 +116,19 @@ export async function runSetup() {
114
116
  }
115
117
  }
116
118
 
117
- writeConfigFile({ apiBase, apiToken: token, workspaceId, defaultProjectId })
118
- console.log(`\nListo. Configuración guardada en ${CONFIG_PATH}.`)
119
+ console.log(`\nEsta conexión se guarda por carpeta -- así puedes tener distintos proyectos de Orquestra`)
120
+ console.log(`en distintos repos sin que se mezclen entre sí.`)
121
+ const folderAnswer = await prompt(
122
+ `¿A qué carpeta aplica? [Enter para usar la actual: ${process.cwd()}]\n` +
123
+ `(usa la carpeta contenedora, ej. la que junta varios repos de un mismo proyecto, si aplica): `
124
+ )
125
+ const expanded = folderAnswer.trim().startsWith('~')
126
+ ? folderAnswer.trim().replace('~', homedir())
127
+ : folderAnswer.trim()
128
+ const folderPath = expanded ? resolve(expanded) : process.cwd()
129
+
130
+ upsertProjectMapping({ apiBase, apiToken: token, workspaceId, folderPath, projectId: defaultProjectId })
131
+ console.log(`\nListo. "${folderPath}" (y sus subcarpetas) ya apunta a este proyecto -- guardado en ${CONFIG_PATH}.`)
119
132
  console.log('\nSiguiente paso -- conéctalo a tu agente de IA:\n')
120
133
  console.log(' Claude Code:')
121
134
  console.log(' claude mcp add orquestra -- npx orquestra-mcp\n')