orquestra-mcp 1.18.0 → 1.18.2

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.18.0",
3
+ "version": "1.18.2",
4
4
  "description": "Servidor MCP de Orquestra -- 89 tools para leer y escribir tareas, ideas, changelog, infraestructura, contenido, QA y más desde un agente de IA",
5
5
  "type": "module",
6
6
  "bin": {
package/src/api.mjs CHANGED
@@ -4,19 +4,23 @@ const DEFAULT_API_BASE = 'https://api.orquestra.me'
4
4
 
5
5
  export function loadConfig() {
6
6
  const fileConfig = readConfigFile()
7
- const apiBase = process.env.ORQUESTRA_API_BASE || fileConfig?.apiBase || DEFAULT_API_BASE
8
- const apiToken = process.env.ORQUESTRA_API_TOKEN || fileConfig?.apiToken
9
- const workspaceId = process.env.ORQUESTRA_WORKSPACE_ID || fileConfig?.workspaceId
7
+ // Todo sale de la MISMA carpeta mapeada (ver resolveProjectForCwd) --
8
+ // apiToken/workspaceId/projectId siempre son de una sola cuenta, nunca
9
+ // el token de una carpeta con el proyecto de otra.
10
+ const folderConfig = resolveProjectForCwd(fileConfig?.projects, process.cwd())
11
+
12
+ const apiBase = process.env.ORQUESTRA_API_BASE || folderConfig?.apiBase || DEFAULT_API_BASE
13
+ const apiToken = process.env.ORQUESTRA_API_TOKEN || folderConfig?.apiToken
14
+ const workspaceId = process.env.ORQUESTRA_WORKSPACE_ID || folderConfig?.workspaceId
10
15
 
11
16
  // ORQUESTRA_DEFAULT_PROJECT_ID (env var, ej. desde un MCP config con
12
17
  // 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())
18
+ // explícita. Si no hay env var, se usa el projectId de la carpeta
19
+ // resuelta arriba. Si ninguna carpeta ancestro está mapeada,
20
+ // defaultProjectId queda undefined a propósito -- resolveProjectId() en
21
+ // server.mjs debe fallar explícito en vez de usar el proyecto de otra
22
+ // carpeta.
23
+ const defaultProjectId = process.env.ORQUESTRA_DEFAULT_PROJECT_ID || folderConfig?.projectId
20
24
 
21
25
  const missing = []
22
26
  if (!apiToken) missing.push('ORQUESTRA_API_TOKEN')
@@ -4,13 +4,40 @@ import { join, dirname, sep } from 'path'
4
4
 
5
5
  export const CONFIG_PATH = join(homedir(), '.orquestra', 'mcp-config.json')
6
6
 
7
+ // Formato viejo (hasta v1.18.1): apiBase/apiToken/workspaceId GLOBALES a
8
+ // nivel raíz, projects={folderPath: projectId}. Eso pisaba la cuenta de
9
+ // TODAS las carpetas cada vez que corrías "setup" con una cuenta distinta
10
+ // -- no había forma real de tener un proyecto de una cuenta y otro de otra
11
+ // cuenta al mismo tiempo. Migra en el momento a projects={folderPath:
12
+ // {apiBase, apiToken, workspaceId, projectId}}, reusando las credenciales
13
+ // globales viejas para cada carpeta ya mapeada (siguen apuntando a la
14
+ // cuenta con la que se logueó por última vez -- si eso ya está mal, hay
15
+ // que volver a correr "setup" en esa carpeta puntual).
16
+ function migrateLegacyConfig(raw) {
17
+ const entries = Object.entries(raw.projects || {})
18
+ if (!entries.some(([, value]) => typeof value === 'string')) return raw
19
+
20
+ const { apiBase, apiToken, workspaceId } = raw
21
+ const projects = {}
22
+ for (const [folderPath, value] of entries) {
23
+ projects[folderPath] = typeof value === 'string'
24
+ ? { apiBase, apiToken, workspaceId, projectId: value }
25
+ : value
26
+ }
27
+ return { projects }
28
+ }
29
+
7
30
  export function readConfigFile() {
8
31
  if (!existsSync(CONFIG_PATH)) return null
32
+ let raw
9
33
  try {
10
- return JSON.parse(readFileSync(CONFIG_PATH, 'utf8'))
34
+ raw = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'))
11
35
  } catch {
12
36
  return null
13
37
  }
38
+ const migrated = migrateLegacyConfig(raw)
39
+ if (migrated !== raw) writeConfigFile(migrated)
40
+ return migrated
14
41
  }
15
42
 
16
43
  export function writeConfigFile(data) {
@@ -18,21 +45,24 @@ export function writeConfigFile(data) {
18
45
  writeFileSync(CONFIG_PATH, JSON.stringify(data, null, 2), { mode: 0o600 })
19
46
  }
20
47
 
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.
48
+ // Guarda/actualiza la cuenta+proyecto de UNA carpeta sin pisar las demás --
49
+ // setup se puede correr varias veces, una por cada carpeta contenedora de
50
+ // repos (ver resolveProjectForCwd), y cada corrida solo debe tocar su
51
+ // propia entrada -- incluida la cuenta (apiToken/workspaceId), para poder
52
+ // tener una carpeta en una cuenta de Orquestra y otra carpeta en otra
53
+ // cuenta distinta al mismo tiempo.
25
54
  export function upsertProjectMapping({ apiBase, apiToken, workspaceId, folderPath, projectId }) {
26
- const existing = readConfigFile() || {}
55
+ const existing = readConfigFile() || { projects: {} }
27
56
  const projects = { ...(existing.projects || {}) }
28
- projects[folderPath] = projectId
29
- writeConfigFile({ apiBase, apiToken, workspaceId, projects })
57
+ projects[folderPath] = { apiBase, apiToken, workspaceId, projectId }
58
+ writeConfigFile({ projects })
30
59
  }
31
60
 
32
61
  // Busca, subiendo desde cwd hacia la raíz, la carpeta mapeada más específica
33
62
  // -- 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.
63
+ // gana, así que un repo individual con su propio mapeo (y su propia cuenta)
64
+ // puede sobreescribir el de la carpeta contenedora que lo engloba. Devuelve
65
+ // el objeto {apiBase, apiToken, workspaceId, projectId} de esa carpeta.
36
66
  export function resolveProjectForCwd(projects, cwd) {
37
67
  if (!projects) return undefined
38
68
  let best = null
package/src/setup.mjs CHANGED
@@ -92,15 +92,16 @@ export async function runSetup() {
92
92
  if (created) {
93
93
  defaultProjectId = body.projectId
94
94
  console.log(`Se creó un workspace y un proyecto default para ti ("${projects[0].name}") -- puedes renombrarlos cuando quieras desde orquestra-web.`)
95
- } else if (projects.length === 1) {
96
- defaultProjectId = projects[0].id
97
- console.log(`Usando tu proyecto "${projects[0].name}" como default.`)
98
95
  } else {
99
96
  console.log('\nElige el proyecto que este MCP va a usar por default:')
100
97
  console.log(' 0. (crear un proyecto nuevo)')
101
98
  projects.forEach((p, i) => console.log(` ${i + 1}. ${p.name}`))
102
- const answer = await prompt('Número: ')
103
- const idx = parseInt(answer, 10)
99
+ const singleDefaultIdx = projects.length === 1 ? 1 : null
100
+ const answer = await prompt(
101
+ singleDefaultIdx ? `Número [Enter para usar "${projects[0].name}"]: ` : 'Número: '
102
+ )
103
+ const trimmed = answer.trim()
104
+ const idx = trimmed === '' && singleDefaultIdx !== null ? singleDefaultIdx : parseInt(trimmed, 10)
104
105
  if (Number.isNaN(idx) || idx < 0 || idx > projects.length) throw new Error('Selección inválida')
105
106
 
106
107
  if (idx === 0) {
@@ -117,6 +118,7 @@ export async function runSetup() {
117
118
  console.log(`Proyecto "${newProject.name}" creado y configurado como default.`)
118
119
  } else {
119
120
  defaultProjectId = projects[idx - 1].id
121
+ console.log(`Usando tu proyecto "${projects[idx - 1].name}" como default.`)
120
122
  }
121
123
  }
122
124