mcp-efficiency-engine 0.1.6 → 0.1.8

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.
@@ -95,6 +95,14 @@ npm install mcp-efficiency-engine
95
95
 
96
96
  Si el entorno bloquea scripts de npm (`allow-scripts`), ejecutar manualmente:
97
97
 
98
+ ```powershell
99
+ npm install mcp-efficiency-engine
100
+ npm approve-scripts mcp-efficiency-engine
101
+ npm rebuild mcp-efficiency-engine
102
+ ```
103
+
104
+ Alternativa manual equivalente:
105
+
98
106
  ```powershell
99
107
  npx mcp-efficiency-engine install
100
108
  npx mcp-efficiency-engine validate -PortableMode
package/README.md CHANGED
@@ -190,13 +190,21 @@ Comportamiento esperado:
190
190
 
191
191
  - copia al proyecto host los artefactos canonicos del engine (`scripts`, `.github`, `.vscode`, `repo-intake`, `orchestrator`, `policies`, `observability`, `autodocs/schema`, `memory`, etc.)
192
192
  - instala motores y herramientas via bootstrap portable
193
- - si no existe `repo-registry/repos.yml`, pregunta por owner/prefix y si quieres registrar un repo inicial para intake
194
- - si no añades repos en ese momento, deja el registry plantilla listo para añadirlos despues y rerun de intake
193
+ - si no existe `repo-registry/repos.yml`, en modo interactivo pregunta por owner/prefix y repo inicial para intake
194
+ - si la instalacion corre en modo no interactivo (comun en lifecycle scripts de npm), crea automaticamente un repo inicial por defecto: dominio `dev`, location `.`
195
195
 
196
196
  Nota npm (entornos con politicas de scripts):
197
197
 
198
- - si `npm` bloquea `install`/`postinstall` (por ejemplo con `allow-scripts`), el scaffold/bootstrapping puede no ejecutarse automaticamente
199
- - en ese caso, aprueba scripts (`npm approve-scripts`) o ejecuta manualmente:
198
+ - si `npm` bloquea `install`/`postinstall` (por ejemplo con `allow-scripts`), el scaffold/bootstrapping no se ejecuta automaticamente
199
+ - flujo recomendado en dos pasos:
200
+
201
+ ```powershell
202
+ npm install mcp-efficiency-engine
203
+ npm approve-scripts mcp-efficiency-engine
204
+ npm rebuild mcp-efficiency-engine
205
+ ```
206
+
207
+ - alternativa manual equivalente:
200
208
 
201
209
  ```powershell
202
210
  npx mcp-efficiency-engine install
@@ -217,6 +225,44 @@ npm install -g mcp-efficiency-engine
217
225
  mcpee install
218
226
  ```
219
227
 
228
+ ### Cuando se conectan boosts/repos auxiliares
229
+
230
+ Resumen rapido:
231
+
232
+ - `npm install` + `rebuild` instala/configura el engine.
233
+ - La conexion real de boosts/repos ocurre al ejecutar intake.
234
+ - Si no configuras repos adicionales, se usa el repo inicial por defecto.
235
+
236
+ Flujo recomendado para nuevos usuarios:
237
+
238
+ - `repo-registry/repos.template.json`: plantilla guiada con dominios y ejemplos.
239
+ - `repo-registry/repos.yml`: registry operativo que consume el intake.
240
+ - `scripts/intake/init-template-registry.cmd`: inicializa `repos.yml` desde la plantilla y pregunta owner/prefix/repo inicial.
241
+
242
+ Pasos recomendados despues de instalar:
243
+
244
+ ```powershell
245
+ # 1) Inicializar registry operativo desde la plantilla (modo asistido)
246
+ .\scripts\intake\init-template-registry.cmd
247
+
248
+ # 2) (Opcional) ajustar repos auxiliares/boosts
249
+ notepad .\repo-registry\repos.yml
250
+
251
+ # 3) materializar capacidades de todos los repos del registry
252
+ .\scripts\intake\run-repo-intake.cmd
253
+
254
+ # 4) validar que el router ya los ve
255
+ .\scripts\ops\hi.ps1
256
+ ```
257
+
258
+ Si quieres preparar entradas manuales, usa los ejemplos de `repo-registry/repos.template.json` (`local`, `github`, `rag_local_github`, `azure_rag_github`) y copialos a `repos.yml`.
259
+
260
+ Si quieres que te pregunte por owner/prefix/repo inicial en modo asistido, ejecuta:
261
+
262
+ ```powershell
263
+ npx mcp-efficiency-engine install
264
+ ```
265
+
220
266
  ### 2) Validación mínima
221
267
 
222
268
  ```powershell
@@ -238,6 +238,15 @@ function deriveRepoPrefix(targetRoot, options) {
238
238
  return `${path.basename(targetRoot).replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase()}_`;
239
239
  }
240
240
 
241
+ function deriveDefaultRepoName(targetRoot, options) {
242
+ if (options.initialRepoName) {
243
+ return options.initialRepoName;
244
+ }
245
+
246
+ const safeBaseName = path.basename(targetRoot).replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
247
+ return `${deriveRepoPrefix(targetRoot, options)}${safeBaseName}`;
248
+ }
249
+
241
250
  function initializeTemplateRegistry(targetRoot, options) {
242
251
  const registryPath = path.join(targetRoot, "repo-registry", "repos.yml");
243
252
  if (fs.existsSync(registryPath)) {
@@ -245,14 +254,28 @@ function initializeTemplateRegistry(targetRoot, options) {
245
254
  }
246
255
 
247
256
  const initScript = path.join(targetRoot, "scripts", "intake", "init-template-registry.ps1");
257
+ const resolvedPrefix = deriveRepoPrefix(targetRoot, options);
248
258
  const args = [
249
259
  "-Owner",
250
260
  options.owner || "your-team",
251
261
  "-RepoNamePrefix",
252
- deriveRepoPrefix(targetRoot, options),
253
- "-SkipInitialRepo",
262
+ resolvedPrefix,
254
263
  ];
255
264
 
265
+ if (options.skipInitialRepo) {
266
+ args.push("-SkipInitialRepo");
267
+ }
268
+ else {
269
+ args.push(
270
+ "-InitialRepoName",
271
+ deriveDefaultRepoName(targetRoot, options),
272
+ "-InitialRepoDomain",
273
+ options.initialRepoDomain || "dev",
274
+ "-InitialRepoLocation",
275
+ options.initialRepoLocation || ".",
276
+ );
277
+ }
278
+
256
279
  return runPowerShell(initScript, args, targetRoot);
257
280
  }
258
281
 
@@ -314,7 +337,8 @@ function runHostInstall(rawOptions) {
314
337
  return hookStatus;
315
338
  }
316
339
 
317
- process.stdout.write("[mcpee] Modo no interactivo detectado. Se inicializo el registry plantilla y se omitio bootstrap interactivo.\n");
340
+ process.stdout.write("[mcpee] Modo no interactivo detectado. Se inicializo repos.yml con un repo inicial por defecto y se omitio bootstrap interactivo.\n");
341
+ process.stdout.write("[mcpee] Si npm bloqueo scripts, ejecuta: npm approve-scripts mcp-efficiency-engine ; npm rebuild mcp-efficiency-engine\n");
318
342
  return 0;
319
343
  }
320
344
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-efficiency-engine",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Motor de orquestacion para agentes MCP con routing por dominio y bootstrap portable.",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",
@@ -1,6 +1,33 @@
1
1
  {
2
2
  "registry_mode": "template",
3
3
  "schema_version": "2.0",
4
+ "onboarding_help": {
5
+ "what_is_this": "Define aqui los repos/boosts que quieres conectar al motor de routing.",
6
+ "quick_start": [
7
+ "1) Duplica un ejemplo de repo_examples dentro de repos[]",
8
+ "2) Ajusta domain, type y location/repo_url",
9
+ "3) Ejecuta scripts/intake/run-repo-intake.cmd",
10
+ "4) Ejecuta scripts/ops/hi.ps1 para validar"
11
+ ],
12
+ "required_fields": [
13
+ "name",
14
+ "domain",
15
+ "type",
16
+ "location (si type=local)",
17
+ "repo_url (si type=github)"
18
+ ],
19
+ "domain_reference": {
20
+ "backend": "Codigo backend en repo unico (CodeGraph)",
21
+ "frontend": "Codigo frontend en repo unico (CodeGraph)",
22
+ "community-content": "Contenido/comunidad/documentacion de apoyo",
23
+ "legacy": "Migracion/impacto multi-repo (GitNexus)",
24
+ "dba": "SQL, esquema y conocimiento de base de datos",
25
+ "iot": "IoT/edge/telemetria",
26
+ "ux-ui": "Patrones de diseno, UX y design system",
27
+ "azure-rag": "Contratos/politicas enterprise con Azure RAG",
28
+ "rag": "RAG local con docs tecnicas"
29
+ }
30
+ },
4
31
  "governance": {
5
32
  "owner": "your-team",
6
33
  "approval_required": false,
@@ -12,7 +39,14 @@
12
39
  "name": "your-prefix_backend_local",
13
40
  "domain": "backend",
14
41
  "type": "local",
15
- "location": "../your-backend-repo"
42
+ "location": "../your-backend-repo",
43
+ "optional": false,
44
+ "dependencies": [],
45
+ "engines": {
46
+ "knowledge": "codegraph",
47
+ "execution": "none",
48
+ "snapshot": "repomix"
49
+ }
16
50
  },
17
51
  "github": {
18
52
  "name": "your-prefix_backend_remote",
@@ -20,7 +54,44 @@
20
54
  "type": "github",
21
55
  "repo_url": "https://github.com/your-org/your-repo.git",
22
56
  "branch": "main",
23
- "cache_location": ".cache/github-repos/your-prefix_backend_remote"
57
+ "cache_location": ".cache/github-repos/your-prefix_backend_remote",
58
+ "optional": false,
59
+ "dependencies": [],
60
+ "engines": {
61
+ "knowledge": "codegraph",
62
+ "execution": "none",
63
+ "snapshot": "repomix"
64
+ }
65
+ },
66
+ "rag_local_github": {
67
+ "name": "your-prefix_rag_local",
68
+ "domain": "rag",
69
+ "type": "github",
70
+ "repo_url": "https://github.com/your-org/your-rag-docs.git",
71
+ "branch": "main",
72
+ "cache_location": ".cache/github-repos/your-prefix_rag_local",
73
+ "optional": true,
74
+ "dependencies": [],
75
+ "engines": {
76
+ "knowledge": "graphify",
77
+ "execution": "none",
78
+ "snapshot": "repomix"
79
+ }
80
+ },
81
+ "azure_rag_github": {
82
+ "name": "your-prefix_azure_rag",
83
+ "domain": "azure-rag",
84
+ "type": "github",
85
+ "repo_url": "https://github.com/your-org/your-enterprise-docs.git",
86
+ "branch": "main",
87
+ "cache_location": ".cache/github-repos/your-prefix_azure_rag",
88
+ "optional": true,
89
+ "dependencies": [],
90
+ "engines": {
91
+ "knowledge": "azure-rag-builder",
92
+ "execution": "none",
93
+ "snapshot": "repomix"
94
+ }
24
95
  }
25
96
  }
26
97
  }
@@ -125,16 +125,30 @@ function Get-DefaultEnginesForDomain {
125
125
  param([Parameter(Mandatory = $true)][string]$Domain)
126
126
 
127
127
  switch ($Domain) {
128
- 'dev' {
128
+ 'backend' {
129
129
  return [pscustomobject]@{
130
130
  knowledge = 'codegraph'
131
131
  execution = 'none'
132
132
  snapshot = 'repomix'
133
133
  }
134
134
  }
135
- 'legacy' {
135
+ 'frontend' {
136
+ return [pscustomobject]@{
137
+ knowledge = 'codegraph'
138
+ execution = 'none'
139
+ snapshot = 'repomix'
140
+ }
141
+ }
142
+ 'community-content' {
136
143
  return [pscustomobject]@{
137
144
  knowledge = 'graphify'
145
+ execution = 'none'
146
+ snapshot = 'repomix'
147
+ }
148
+ }
149
+ 'legacy' {
150
+ return [pscustomobject]@{
151
+ knowledge = 'gitnexus'
138
152
  execution = 'gitnexus'
139
153
  snapshot = 'repomix'
140
154
  }
@@ -160,6 +174,20 @@ function Get-DefaultEnginesForDomain {
160
174
  snapshot = 'repomix'
161
175
  }
162
176
  }
177
+ 'rag' {
178
+ return [pscustomobject]@{
179
+ knowledge = 'graphify'
180
+ execution = 'none'
181
+ snapshot = 'repomix'
182
+ }
183
+ }
184
+ 'ux-ui' {
185
+ return [pscustomobject]@{
186
+ knowledge = 'graphify'
187
+ execution = 'none'
188
+ snapshot = 'repomix'
189
+ }
190
+ }
163
191
  default {
164
192
  return [pscustomobject]@{
165
193
  knowledge = 'codegraph'
@@ -218,11 +246,11 @@ else {
218
246
  $templateRegistry.repos = @()
219
247
  if ($addInitialRepo) {
220
248
  $defaultRepoName = if ([string]::IsNullOrWhiteSpace($InitialRepoName)) { "$resolvedPrefix$((Split-Path $repoRoot -Leaf).ToLowerInvariant())" } else { $InitialRepoName }
221
- $defaultRepoDomain = if ([string]::IsNullOrWhiteSpace($InitialRepoDomain)) { 'dev' } else { $InitialRepoDomain }
249
+ $defaultRepoDomain = if ([string]::IsNullOrWhiteSpace($InitialRepoDomain)) { 'backend' } else { $InitialRepoDomain }
222
250
  $defaultRepoLocation = if ([string]::IsNullOrWhiteSpace($InitialRepoLocation)) { '.' } else { $InitialRepoLocation }
223
251
 
224
252
  $resolvedRepoName = Read-RequiredValue -Prompt 'Initial repo name' -DefaultValue $defaultRepoName -ProvidedValue $InitialRepoName
225
- $resolvedRepoDomain = Read-ChoiceValue -Prompt 'Initial repo domain' -AllowedValues @('dev', 'legacy', 'dba', 'iot', 'azure-rag') -DefaultValue $defaultRepoDomain -ProvidedValue $InitialRepoDomain
253
+ $resolvedRepoDomain = Read-ChoiceValue -Prompt 'Initial repo domain' -AllowedValues @('backend', 'frontend', 'community-content', 'legacy', 'dba', 'iot', 'ux-ui', 'azure-rag', 'rag') -DefaultValue $defaultRepoDomain -ProvidedValue $InitialRepoDomain
226
254
  $resolvedRepoLocation = Read-RequiredValue -Prompt 'Initial repo location' -DefaultValue $defaultRepoLocation -ProvidedValue $InitialRepoLocation
227
255
  $resolvedEngines = Get-DefaultEnginesForDomain -Domain $resolvedRepoDomain
228
256
 
@@ -569,6 +569,10 @@ function Ensure-CodegraphInitialized {
569
569
  }
570
570
 
571
571
  function Ensure-SetupPrerequisites {
572
+ param(
573
+ [string]$Reason = 'missing prerequisite'
574
+ )
575
+
572
576
  if ($script:SetupAttempted) {
573
577
  if (-not $script:SetupSucceeded) {
574
578
  throw 'setup-prerequisites was already attempted and failed earlier in this run'
@@ -577,11 +581,15 @@ function Ensure-SetupPrerequisites {
577
581
  }
578
582
 
579
583
  $script:SetupAttempted = $true
580
- Write-Host '[info] Missing prerequisite detected. Running setup-prerequisites automatically...' -ForegroundColor DarkYellow
581
- & pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\setup\setup-prerequisites.ps1
582
- if ($LASTEXITCODE -ne 0) {
584
+ Write-Host ("[info] Missing prerequisite detected ({0}). Running setup-prerequisites automatically..." -f $Reason) -ForegroundColor DarkYellow
585
+ try {
586
+ $script:StepLogs['setup-prerequisites-auto'] = Invoke-LoggedAction -StepName 'setup-prerequisites-auto' -InfoLines @("Auto-recovery reason: $Reason") -Action {
587
+ & pwsh -NoProfile -ExecutionPolicy Bypass -File .\scripts\setup\setup-prerequisites.ps1 -VerboseTrace
588
+ }
589
+ }
590
+ catch {
583
591
  $script:SetupSucceeded = $false
584
- throw "setup-prerequisites failed with exit code $LASTEXITCODE"
592
+ throw $_.Exception.Message
585
593
  }
586
594
 
587
595
  $script:SetupSucceeded = $true
@@ -656,7 +664,7 @@ Invoke-Step -Name 'Validate context' -Action $validateContextAction -Required $t
656
664
  if ($script:HasFailures) {
657
665
  $script:HasFailures = $false
658
666
  Invoke-Step -Name 'Setup prerequisites' -Action {
659
- [void](Ensure-SetupPrerequisites)
667
+ [void](Ensure-SetupPrerequisites -Reason 'validate-context failed')
660
668
  } -Required $true
661
669
 
662
670
  Invoke-Step -Name 'Validate context (retry)' -Action $validateContextAction -Required $true
@@ -691,7 +699,7 @@ Invoke-Step -Name 'Validate memory/cache artifacts' -Action {
691
699
  }
692
700
 
693
701
  if (-not (Get-Command codebase-memory-mcp -ErrorAction SilentlyContinue)) {
694
- [void](Ensure-SetupPrerequisites)
702
+ [void](Ensure-SetupPrerequisites -Reason 'codebase-memory-mcp command not found')
695
703
  }
696
704
 
697
705
  if (-not (Get-Command codebase-memory-mcp -ErrorAction SilentlyContinue)) {
@@ -701,7 +709,7 @@ Invoke-Step -Name 'Validate memory/cache artifacts' -Action {
701
709
 
702
710
  Invoke-Step -Name 'Activate token-saver mode' -Action {
703
711
  if (-not (Test-CommandAvailable -Name 'token-saver-mcp')) {
704
- [void](Ensure-SetupPrerequisites)
712
+ [void](Ensure-SetupPrerequisites -Reason 'token-saver-mcp command not found')
705
713
  }
706
714
 
707
715
  if (-not (Test-CommandAvailable -Name 'token-saver-mcp')) {
@@ -5,7 +5,8 @@ param(
5
5
  [switch]$SkipGitnexus,
6
6
  [switch]$SkipGraphify,
7
7
  [switch]$SkipRepomix,
8
- [switch]$PortableMode
8
+ [switch]$PortableMode,
9
+ [switch]$VerboseTrace
9
10
  )
10
11
 
11
12
  $ErrorActionPreference = 'Stop'
@@ -20,6 +21,17 @@ function Test-Command {
20
21
  return [bool](Get-Command -Name $Name -ErrorAction SilentlyContinue)
21
22
  }
22
23
 
24
+ function Write-TraceStep {
25
+ param(
26
+ [Parameter(Mandatory = $true)][string]$Message
27
+ )
28
+
29
+ if ($VerboseTrace) {
30
+ $stamp = (Get-Date).ToString('HH:mm:ss')
31
+ Write-Host ("[trace {0}] {1}" -f $stamp, $Message) -ForegroundColor DarkGray
32
+ }
33
+ }
34
+
23
35
  function Install-NpmGlobalPackage {
24
36
  param(
25
37
  [Parameter(Mandatory = $true)][string]$Package,
@@ -143,7 +155,29 @@ function Ensure-PythonModuleInstalled {
143
155
  }
144
156
  }
145
157
 
158
+ function Sync-GraphifyArtifactsFromScripts {
159
+ $scriptsGraphPath = 'scripts/graphify-out/graph.json'
160
+ $contextGraphDir = 'context/graphify-out'
161
+ $contextGraphPath = Join-Path $contextGraphDir 'graph.json'
162
+
163
+ if (-not (Test-Path $scriptsGraphPath)) {
164
+ return $false
165
+ }
166
+
167
+ New-Item -ItemType Directory -Path $contextGraphDir -Force | Out-Null
168
+ Copy-Item -Path $scriptsGraphPath -Destination $contextGraphPath -Force
169
+
170
+ $scriptsManifestPath = 'scripts/graphify-out/manifest.json'
171
+ $contextManifestPath = Join-Path $contextGraphDir 'manifest.json'
172
+ if (Test-Path $scriptsManifestPath) {
173
+ Copy-Item -Path $scriptsManifestPath -Destination $contextManifestPath -Force
174
+ }
175
+
176
+ return $true
177
+ }
178
+
146
179
  Write-Host '== MCP platform prerequisites setup =='
180
+ Write-TraceStep -Message ("repoRoot={0}" -f $repoRoot)
147
181
 
148
182
  $toolingManifest = Get-ToolingManifest -Path $toolingManifestPath
149
183
  $externalCliEntries = @($toolingManifest.external_clis)
@@ -153,14 +187,17 @@ foreach ($tool in $externalCliEntries) {
153
187
  }
154
188
 
155
189
  if (-not $SkipCodebaseMemory) {
190
+ Write-TraceStep -Message 'Installing codebase-memory-mcp from tooling manifest'
156
191
  Install-ToolFromManifest -Tool $toolByCommand['codebase-memory-mcp']
157
192
  }
158
193
 
159
194
  if (-not $SkipTokenSaver) {
195
+ Write-TraceStep -Message 'Installing token-saver-mcp from tooling manifest'
160
196
  Install-ToolFromManifest -Tool $toolByCommand['token-saver-mcp']
161
197
  }
162
198
 
163
199
  if (-not $SkipCodegraph) {
200
+ Write-TraceStep -Message 'Installing/verifying codegraph'
164
201
  Install-ToolFromManifest -Tool $toolByCommand['codegraph']
165
202
  Write-Host '[setup] codegraph install'
166
203
  codegraph install
@@ -174,12 +211,14 @@ if (-not $SkipCodegraph) {
174
211
  }
175
212
 
176
213
  if (-not $SkipGitnexus) {
214
+ Write-TraceStep -Message 'Installing/verifying gitnexus'
177
215
  Install-ToolFromManifest -Tool $toolByCommand['gitnexus']
178
216
  Write-Host '[setup] gitnexus setup'
179
217
  gitnexus setup
180
218
  }
181
219
 
182
220
  if (-not $SkipRepomix) {
221
+ Write-TraceStep -Message 'Installing/verifying repomix'
183
222
  Install-ToolFromManifest -Tool $toolByCommand['repomix']
184
223
  }
185
224
 
@@ -198,9 +237,11 @@ else {
198
237
  }
199
238
 
200
239
  # Core observability dependency used by KPI publishing and telemetry export.
240
+ Write-TraceStep -Message 'Ensuring Python module langsmith'
201
241
  Ensure-PythonModuleInstalled -PythonCommand $pyCmd -PythonArgs $pyArgs -Module 'langsmith' -Package 'langsmith'
202
242
 
203
243
  if (-not $SkipGraphify) {
244
+ Write-TraceStep -Message 'Installing Python requirements for graphify runtime'
204
245
  Install-PythonRequirements -PythonCommand $pyCmd -PythonArgs $pyArgs -RequirementsPath 'requirements.txt'
205
246
 
206
247
  if (-not (Test-PythonImport -PythonCommand $pyCmd -PythonArgs $pyArgs -Module 'graphify.serve')) {
@@ -212,11 +253,20 @@ if (-not $SkipGraphify) {
212
253
  }
213
254
 
214
255
  if (-not (Test-Path 'context/graphify-out/graph.json')) {
215
- Write-Host '[setup] graphify extract scripts --no-cluster --out context'
216
- & $pyCmd @pyArgs -m graphify extract scripts --no-cluster --out context
256
+ Write-Host '[setup] graphify update scripts --no-cluster'
257
+ Write-TraceStep -Message ("Running: {0} {1} -m graphify update scripts --no-cluster" -f $pyCmd, ($pyArgs -join ' '))
258
+ & $pyCmd @pyArgs -m graphify update scripts --no-cluster
259
+ if ($LASTEXITCODE -ne 0) {
260
+ throw "graphify update failed with exit code $LASTEXITCODE"
261
+ }
262
+
263
+ if (Sync-GraphifyArtifactsFromScripts) {
264
+ Write-Host '[ok] graphify output synced from scripts/graphify-out to context/graphify-out'
265
+ }
217
266
  }
218
267
  else {
219
268
  Write-Host '[ok] context/graphify-out/graph.json already exists'
269
+ Write-TraceStep -Message 'Skipping graphify update because context graph already exists'
220
270
  }
221
271
  }
222
272
 
@@ -69,6 +69,44 @@ function Write-SetupValidationReport {
69
69
  $Report | ConvertTo-Json -Depth 20 | Set-Content -Path $Path -Encoding utf8
70
70
  }
71
71
 
72
+ function Sync-GraphifyArtifactsIfNeeded {
73
+ $contextGraphDir = Join-Path $repoRoot 'context/graphify-out'
74
+ $contextGraphPath = Join-Path $contextGraphDir 'graph.json'
75
+ $scriptsGraphDir = Join-Path $repoRoot 'scripts/graphify-out'
76
+ $scriptsGraphPath = Join-Path $scriptsGraphDir 'graph.json'
77
+
78
+ if ((-not (Test-Path $contextGraphPath)) -and (Test-Path $scriptsGraphPath)) {
79
+ New-Item -ItemType Directory -Path $contextGraphDir -Force | Out-Null
80
+ Copy-Item -Path $scriptsGraphPath -Destination $contextGraphPath -Force
81
+
82
+ $scriptsManifestPath = Join-Path $scriptsGraphDir 'manifest.json'
83
+ $contextManifestPath = Join-Path $contextGraphDir 'manifest.json'
84
+ if ((-not (Test-Path $contextManifestPath)) -and (Test-Path $scriptsManifestPath)) {
85
+ Copy-Item -Path $scriptsManifestPath -Destination $contextManifestPath -Force
86
+ }
87
+ }
88
+ }
89
+
90
+ function Resolve-PythonLauncher {
91
+ if (Get-Command py -ErrorAction SilentlyContinue) {
92
+ return [pscustomobject]@{
93
+ command = 'py'
94
+ args = @('-3')
95
+ printable = 'py -3'
96
+ }
97
+ }
98
+
99
+ if (Get-Command python -ErrorAction SilentlyContinue) {
100
+ return [pscustomobject]@{
101
+ command = 'python'
102
+ args = @()
103
+ printable = 'python'
104
+ }
105
+ }
106
+
107
+ return $null
108
+ }
109
+
72
110
  # Ensure report directory exists
73
111
  $reportDir = Split-Path -Parent $setupValidationReportPath
74
112
  if (-not (Test-Path $reportDir)) {
@@ -192,8 +230,9 @@ if ($toolingManifest -and ($toolingManifest.PSObject.Properties.Name -contains '
192
230
 
193
231
  try {
194
232
  $mcpRaw = Get-Content -Raw -Path '.vscode/mcp.json' | ConvertFrom-Json -Depth 20
195
- if ($mcpRaw.servers.gitnexus.command -ne 'gitnexus') {
196
- $errors += 'MCP gitnexus command should be local `gitnexus` (avoid npx startup latency).'
233
+ $gitnexusCommand = [string]$mcpRaw.servers.gitnexus.command
234
+ if ($gitnexusCommand -eq 'npx') {
235
+ $errors += 'MCP gitnexus command should avoid `npx` (startup latency). Use local `gitnexus` or local wrapper script.'
197
236
  }
198
237
  $scanLimit = 0
199
238
  $gitnexusHasEnv = ($mcpRaw.servers.gitnexus.PSObject.Properties.Name -contains 'env')
@@ -215,6 +254,8 @@ catch {
215
254
  $errors += "Unable to parse .vscode/mcp.json: $($_.Exception.Message)"
216
255
  }
217
256
 
257
+ Sync-GraphifyArtifactsIfNeeded
258
+
218
259
  $specFiles = @(
219
260
  "specs/architecture.spec.md",
220
261
  "specs/azure-rag.spec.md",
@@ -312,29 +353,32 @@ if (-not (Get-Command py -ErrorAction SilentlyContinue) -and -not (Get-Command p
312
353
  }
313
354
  }
314
355
  else {
315
- $pythonCmd = "python"
316
- $pythonArgs = @("-c", "import graphify.serve, mcp; print('ok')")
317
- if (Get-Command py -ErrorAction SilentlyContinue) {
318
- $pythonCmd = "py"
319
- $pythonArgs = @("-3.14", "-c", "import graphify.serve, mcp; print('ok')")
320
- }
356
+ $pythonLauncher = Resolve-PythonLauncher
357
+ $pythonCmd = [string]$pythonLauncher.command
358
+ $pythonArgs = @($pythonLauncher.args + @("-c", "import graphify.serve, mcp; print('ok')"))
321
359
 
322
360
  & $pythonCmd @pythonArgs *> $null
323
361
  $setupValidationReport.python_modules += @{
324
- launcher = $pythonCmd
362
+ launcher = [string]$pythonLauncher.printable
325
363
  args = $pythonArgs
326
364
  import_ok = ($LASTEXITCODE -eq 0)
327
365
  }
328
366
  if ($LASTEXITCODE -ne 0) {
329
367
  if (-not ($CIMode)) {
330
- $errors += 'Graphify MCP runtime missing. Run: py -3.14 -m pip install -r requirements.txt'
368
+ $errors += "Graphify MCP runtime missing. Run: $($pythonLauncher.printable) -m pip install -r requirements.txt"
331
369
  }
332
370
  }
333
371
  }
334
372
 
335
- if (-not (Test-Path "context/graphify-out/graph.json")) {
373
+ if ((-not (Test-Path "context/graphify-out/graph.json")) -and (-not (Test-Path "scripts/graphify-out/graph.json"))) {
336
374
  if (-not ($CIMode)) {
337
- $errors += "Missing context/graphify-out/graph.json. Run: py -3.14 -m graphify extract scripts --no-cluster --out context"
375
+ $pythonLauncher = Resolve-PythonLauncher
376
+ if ($null -ne $pythonLauncher) {
377
+ $errors += "Missing graphify graph output. Run: $($pythonLauncher.printable) -m graphify update scripts --no-cluster"
378
+ }
379
+ else {
380
+ $errors += 'Missing graphify graph output. Run: python -m graphify update scripts --no-cluster'
381
+ }
338
382
  }
339
383
  }
340
384