mcp-efficiency-engine 0.1.5 → 0.1.7
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/.vscode/mcp.json +12 -5
- package/FINAL_USAGE_GUIDE.md +8 -0
- package/README.md +50 -4
- package/bin/install-host.js +27 -3
- package/bin/mcpee.js +107 -27
- package/package.json +1 -1
- package/scripts/ops/emit-terminal-command-telemetry.py +84 -0
- package/scripts/ops/install-terminal-telemetry-hook.ps1 +152 -0
- package/scripts/ops/migrate-localdb-to-azure-sql.ps1 +98 -0
- package/scripts/ops/start-codegraph-mcp.ps1 +67 -0
- package/scripts/ops/start-gitnexus-mcp.ps1 +85 -0
- package/scripts/ops/uninstall-terminal-telemetry-hook.ps1 +53 -0
- package/scripts/setup/setup-prerequisites.ps1 +30 -2
- package/scripts/setup/validate-context.ps1 +25 -4
package/.vscode/mcp.json
CHANGED
|
@@ -7,16 +7,23 @@
|
|
|
7
7
|
"command": "codebase-memory-mcp"
|
|
8
8
|
},
|
|
9
9
|
"codegraph": {
|
|
10
|
-
"command": "
|
|
10
|
+
"command": "pwsh",
|
|
11
11
|
"args": [
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"-NoProfile",
|
|
13
|
+
"-ExecutionPolicy",
|
|
14
|
+
"Bypass",
|
|
15
|
+
"-File",
|
|
16
|
+
"scripts/ops/start-codegraph-mcp.ps1"
|
|
14
17
|
]
|
|
15
18
|
},
|
|
16
19
|
"gitnexus": {
|
|
17
|
-
"command": "
|
|
20
|
+
"command": "pwsh",
|
|
18
21
|
"args": [
|
|
19
|
-
"
|
|
22
|
+
"-NoProfile",
|
|
23
|
+
"-ExecutionPolicy",
|
|
24
|
+
"Bypass",
|
|
25
|
+
"-File",
|
|
26
|
+
"scripts/ops/start-gitnexus-mcp.ps1"
|
|
20
27
|
],
|
|
21
28
|
"env": {
|
|
22
29
|
"GITNEXUS_SEMANTIC_EXACT_SCAN_LIMIT": "50000",
|
package/FINAL_USAGE_GUIDE.md
CHANGED
|
@@ -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
|
|
194
|
-
- si no
|
|
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
|
|
199
|
-
-
|
|
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,33 @@ 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
|
+
Pasos recomendados despues de instalar:
|
|
237
|
+
|
|
238
|
+
```powershell
|
|
239
|
+
# 1) (Opcional) editar repos auxiliares/boosts
|
|
240
|
+
notepad .\repo-registry\repos.yml
|
|
241
|
+
|
|
242
|
+
# 2) materializar capacidades de todos los repos del registry
|
|
243
|
+
.\scripts\intake\run-repo-intake.cmd
|
|
244
|
+
|
|
245
|
+
# 3) validar que el router ya los ve
|
|
246
|
+
.\scripts\ops\hi.ps1
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Si quieres que te pregunte por owner/prefix/repo inicial en modo asistido, ejecuta:
|
|
250
|
+
|
|
251
|
+
```powershell
|
|
252
|
+
npx mcp-efficiency-engine install
|
|
253
|
+
```
|
|
254
|
+
|
|
220
255
|
### 2) Validación mínima
|
|
221
256
|
|
|
222
257
|
```powershell
|
|
@@ -233,6 +268,17 @@ py -3 .\scripts\intake\run-routing-evals.py
|
|
|
233
268
|
.\scripts\ops\bye.ps1
|
|
234
269
|
```
|
|
235
270
|
|
|
271
|
+
Telemetría de terminal (PowerShell, opcional):
|
|
272
|
+
|
|
273
|
+
```powershell
|
|
274
|
+
mcpee observe-on
|
|
275
|
+
# ... comandos interactivos ...
|
|
276
|
+
mcpee observe-off
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
- `mcpee observe-on` instala un hook global de perfil PowerShell para emitir un evento de telemetría por comando usando `scripts/ops/emit-terminal-command-telemetry.py`.
|
|
280
|
+
- `mcpee observe-off` elimina el hook global y restaura el perfil sin instrumentación.
|
|
281
|
+
|
|
236
282
|
Validación extendida recomendada:
|
|
237
283
|
|
|
238
284
|
```powershell
|
package/bin/install-host.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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/bin/mcpee.js
CHANGED
|
@@ -12,8 +12,77 @@ const commandMap = {
|
|
|
12
12
|
hi: "scripts/ops/hi.ps1",
|
|
13
13
|
bye: "scripts/ops/bye.ps1",
|
|
14
14
|
intake: "scripts/intake/run-repo-intake.ps1",
|
|
15
|
+
"observe-on": "scripts/ops/install-terminal-telemetry-hook.ps1",
|
|
16
|
+
"observe-off": "scripts/ops/uninstall-terminal-telemetry-hook.ps1",
|
|
15
17
|
};
|
|
16
18
|
|
|
19
|
+
function resolveSessionId() {
|
|
20
|
+
const explicitSessionId = (process.env.MCP_SESSION_ID || "").trim();
|
|
21
|
+
if (explicitSessionId) {
|
|
22
|
+
return explicitSessionId;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const sessionLogHint = (process.env.VSCODE_TARGET_SESSION_LOG || "").trim();
|
|
26
|
+
if (sessionLogHint) {
|
|
27
|
+
const match = sessionLogHint.match(/[0-9a-fA-F-]{36}/);
|
|
28
|
+
if (match && match[0]) {
|
|
29
|
+
return match[0];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return "mcpee-cli";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolvePythonCandidates() {
|
|
37
|
+
if (process.platform === "win32") {
|
|
38
|
+
return [
|
|
39
|
+
["py", ["-3"]],
|
|
40
|
+
["python", []],
|
|
41
|
+
["python3", []],
|
|
42
|
+
];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
["python3", []],
|
|
47
|
+
["python", []],
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function runCopilotUsageIngest(repoRootPath) {
|
|
52
|
+
const sessionLogHint = (process.env.VSCODE_TARGET_SESSION_LOG || "").trim();
|
|
53
|
+
if (!sessionLogHint) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const ingestScriptPath = path.join(repoRootPath, "scripts", "learning", "ingest-copilot-session-usage.py");
|
|
58
|
+
if (!fs.existsSync(ingestScriptPath)) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
for (const [pythonCommand, pythonPrefixArgs] of resolvePythonCandidates()) {
|
|
63
|
+
const ingestResult = spawnSync(
|
|
64
|
+
pythonCommand,
|
|
65
|
+
[
|
|
66
|
+
...pythonPrefixArgs,
|
|
67
|
+
ingestScriptPath,
|
|
68
|
+
"--session-log",
|
|
69
|
+
sessionLogHint,
|
|
70
|
+
],
|
|
71
|
+
{
|
|
72
|
+
cwd: repoRootPath,
|
|
73
|
+
stdio: "ignore",
|
|
74
|
+
},
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
if (ingestResult.error && ingestResult.error.code === "ENOENT") {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// La ingesta es best-effort: si falla no bloquea el comando principal.
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
17
86
|
function resolveExecutionRoot(scriptRelativePath) {
|
|
18
87
|
const configuredTarget = process.env.MCPEE_TARGET_DIR ? path.resolve(process.env.MCPEE_TARGET_DIR) : "";
|
|
19
88
|
const cwdRoot = process.cwd();
|
|
@@ -41,6 +110,8 @@ function printHelp() {
|
|
|
41
110
|
" hi Ejecuta el preflight diario.",
|
|
42
111
|
" bye Ejecuta el cierre diario.",
|
|
43
112
|
" intake Ejecuta el repo intake.",
|
|
113
|
+
" observe-on Activa telemetria global del terminal (hook PowerShell).",
|
|
114
|
+
" observe-off Desactiva telemetria global del terminal.",
|
|
44
115
|
"",
|
|
45
116
|
"Ejemplos:",
|
|
46
117
|
" npx mcp-efficiency-engine bootstrap",
|
|
@@ -61,6 +132,10 @@ function runPowerShellScript(scriptRelativePath, forwardedArgs) {
|
|
|
61
132
|
const executionRoot = resolveExecutionRoot(scriptRelativePath);
|
|
62
133
|
const scriptPath = path.join(executionRoot, scriptRelativePath);
|
|
63
134
|
const tracerScriptPath = path.join(repoRoot, "scripts", "ops", "trace-command.py");
|
|
135
|
+
const sessionId = resolveSessionId();
|
|
136
|
+
|
|
137
|
+
runCopilotUsageIngest(repoRoot);
|
|
138
|
+
|
|
64
139
|
if (!fs.existsSync(scriptPath)) {
|
|
65
140
|
process.stderr.write(`Script no encontrado: ${scriptRelativePath}\n`);
|
|
66
141
|
return 1;
|
|
@@ -70,37 +145,42 @@ function runPowerShellScript(scriptRelativePath, forwardedArgs) {
|
|
|
70
145
|
for (const shellCommand of shellCandidates) {
|
|
71
146
|
if (fs.existsSync(tracerScriptPath)) {
|
|
72
147
|
const operation = `mcpee.${path.basename(scriptRelativePath, path.extname(scriptRelativePath))}`;
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
148
|
+
for (const [pythonCommand, pythonPrefixArgs] of resolvePythonCandidates()) {
|
|
149
|
+
const tracedResult = spawnSync(
|
|
150
|
+
pythonCommand,
|
|
151
|
+
[
|
|
152
|
+
...pythonPrefixArgs,
|
|
153
|
+
tracerScriptPath,
|
|
154
|
+
"--operation",
|
|
155
|
+
operation,
|
|
156
|
+
"--session-id",
|
|
157
|
+
sessionId,
|
|
158
|
+
"--cwd",
|
|
159
|
+
executionRoot,
|
|
160
|
+
"--",
|
|
161
|
+
shellCommand,
|
|
162
|
+
"-NoProfile",
|
|
163
|
+
"-ExecutionPolicy",
|
|
164
|
+
"Bypass",
|
|
165
|
+
"-File",
|
|
166
|
+
scriptPath,
|
|
167
|
+
...forwardedArgs,
|
|
168
|
+
],
|
|
169
|
+
{
|
|
170
|
+
cwd: repoRoot,
|
|
171
|
+
stdio: "inherit",
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
if (tracedResult.error && tracedResult.error.code === "ENOENT") {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
100
179
|
if (tracedResult.error) {
|
|
101
180
|
process.stderr.write(`${tracedResult.error.message}\n`);
|
|
102
181
|
return 1;
|
|
103
182
|
}
|
|
183
|
+
|
|
104
184
|
return tracedResult.status ?? 0;
|
|
105
185
|
}
|
|
106
186
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import hashlib
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _bootstrap_repo_root() -> Path:
|
|
10
|
+
repo_root = Path(__file__).resolve().parents[2]
|
|
11
|
+
if str(repo_root) not in sys.path:
|
|
12
|
+
sys.path.insert(0, str(repo_root))
|
|
13
|
+
return repo_root
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _hash_command(command: str) -> str:
|
|
17
|
+
return hashlib.sha256(command.encode("utf-8")).hexdigest()[:16]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def main() -> int:
|
|
21
|
+
parser = argparse.ArgumentParser(description="Emit telemetry for an already executed terminal command.")
|
|
22
|
+
parser.add_argument("--session-id", default="terminal-session", help="Session identifier")
|
|
23
|
+
parser.add_argument("--cwd", default=".", help="Current working directory")
|
|
24
|
+
parser.add_argument("--command", default="", help="Executed command text")
|
|
25
|
+
parser.add_argument("--success", default="true", help="Whether command succeeded (true/false)")
|
|
26
|
+
parser.add_argument("--exit-code", type=int, default=0, help="Exit code for native commands")
|
|
27
|
+
args = parser.parse_args()
|
|
28
|
+
|
|
29
|
+
command_text = (args.command or "").strip()
|
|
30
|
+
if not command_text:
|
|
31
|
+
return 0
|
|
32
|
+
|
|
33
|
+
repo_root = _bootstrap_repo_root()
|
|
34
|
+
from telemetry import build_telemetry_collector
|
|
35
|
+
|
|
36
|
+
collector = build_telemetry_collector(repo_root)
|
|
37
|
+
|
|
38
|
+
success = str(args.success).strip().lower() in {"1", "true", "yes", "ok"}
|
|
39
|
+
normalized_cwd = str(Path(args.cwd).resolve())
|
|
40
|
+
|
|
41
|
+
with collector.start_execution(
|
|
42
|
+
operation="terminal.command",
|
|
43
|
+
session_id=str(args.session_id).strip() or "terminal-session",
|
|
44
|
+
model="powershell",
|
|
45
|
+
provider="local-shell",
|
|
46
|
+
):
|
|
47
|
+
with collector.start_span(
|
|
48
|
+
name="terminal.executed_command",
|
|
49
|
+
kind="TOOL",
|
|
50
|
+
attributes={
|
|
51
|
+
"cwd": normalized_cwd,
|
|
52
|
+
"command_hash": _hash_command(command_text),
|
|
53
|
+
"success": success,
|
|
54
|
+
"exit_code": int(args.exit_code),
|
|
55
|
+
},
|
|
56
|
+
):
|
|
57
|
+
collector.record_event(
|
|
58
|
+
"RoutingResolved",
|
|
59
|
+
{
|
|
60
|
+
"source": "terminal-hook",
|
|
61
|
+
"cwd": normalized_cwd,
|
|
62
|
+
"success": success,
|
|
63
|
+
"exit_code": int(args.exit_code),
|
|
64
|
+
},
|
|
65
|
+
level="INFO",
|
|
66
|
+
)
|
|
67
|
+
collector.record_metric("terminal_command_count", 1.0, unit="count")
|
|
68
|
+
collector.record_metric("terminal_last_exit_code", float(int(args.exit_code)), unit="code")
|
|
69
|
+
if not success:
|
|
70
|
+
collector.record_event(
|
|
71
|
+
"ExceptionThrown",
|
|
72
|
+
{
|
|
73
|
+
"source": "terminal-hook",
|
|
74
|
+
"message": "command_reported_failure",
|
|
75
|
+
"exit_code": int(args.exit_code),
|
|
76
|
+
},
|
|
77
|
+
level="ERROR",
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$AllHosts,
|
|
3
|
+
[switch]$CurrentProcess,
|
|
4
|
+
[string]$SessionId = ''
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
Set-StrictMode -Version Latest
|
|
8
|
+
$ErrorActionPreference = 'Stop'
|
|
9
|
+
|
|
10
|
+
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
11
|
+
$sessionIdResolved = if ([string]::IsNullOrWhiteSpace($SessionId)) {
|
|
12
|
+
if (-not [string]::IsNullOrWhiteSpace($env:MCP_SESSION_ID)) { $env:MCP_SESSION_ID }
|
|
13
|
+
elseif (-not [string]::IsNullOrWhiteSpace($env:VSCODE_TARGET_SESSION_LOG) -and $env:VSCODE_TARGET_SESSION_LOG -match '([0-9a-fA-F-]{36})') { $Matches[1] }
|
|
14
|
+
else { 'terminal-session' }
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
$SessionId
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
$hookBlock = @"
|
|
21
|
+
# >>> MCPEE_TERMINAL_TELEMETRY_START >>>
|
|
22
|
+
`$global:MCPEE_TELEMETRY_REPO_ROOT = '$repoRoot'
|
|
23
|
+
`$global:MCPEE_TELEMETRY_SESSION_ID = '$sessionIdResolved'
|
|
24
|
+
`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID = -1
|
|
25
|
+
`$global:MCPEE_TELEMETRY_LAST_STAMP = ''
|
|
26
|
+
|
|
27
|
+
function global:Get-McpeeTelemetryPythonCommand {
|
|
28
|
+
if (Get-Command py -ErrorAction SilentlyContinue) {
|
|
29
|
+
return @('py', '-3')
|
|
30
|
+
}
|
|
31
|
+
if (Get-Command python -ErrorAction SilentlyContinue) {
|
|
32
|
+
return @('python')
|
|
33
|
+
}
|
|
34
|
+
if (Get-Command python3 -ErrorAction SilentlyContinue) {
|
|
35
|
+
return @('python3')
|
|
36
|
+
}
|
|
37
|
+
return @()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function global:Invoke-McpeeTerminalTelemetry {
|
|
41
|
+
try {
|
|
42
|
+
`$last = Get-History -Count 1 -ErrorAction SilentlyContinue
|
|
43
|
+
if (`$null -eq `$last) { return }
|
|
44
|
+
|
|
45
|
+
`$historyId = [int]`$last.Id
|
|
46
|
+
if (`$historyId -le [int]`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID) { return }
|
|
47
|
+
|
|
48
|
+
`$commandText = [string]`$last.CommandLine
|
|
49
|
+
if ([string]::IsNullOrWhiteSpace(`$commandText)) { return }
|
|
50
|
+
|
|
51
|
+
`$commandTrim = `$commandText.Trim()
|
|
52
|
+
if (`$commandTrim -match 'Invoke-McpeeTerminalTelemetry|emit-terminal-command-telemetry\.py') {
|
|
53
|
+
`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID = `$historyId
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
`$stamp = ('{0}:{1}' -f `$historyId, `$commandTrim)
|
|
58
|
+
if (`$stamp -eq `$global:MCPEE_TELEMETRY_LAST_STAMP) {
|
|
59
|
+
`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID = `$historyId
|
|
60
|
+
return
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
`$py = Get-McpeeTelemetryPythonCommand
|
|
64
|
+
if (`$py.Count -eq 0) {
|
|
65
|
+
`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID = `$historyId
|
|
66
|
+
`$global:MCPEE_TELEMETRY_LAST_STAMP = `$stamp
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
`$scriptPath = Join-Path `$global:MCPEE_TELEMETRY_REPO_ROOT 'scripts/ops/emit-terminal-command-telemetry.py'
|
|
71
|
+
if (-not (Test-Path `$scriptPath)) {
|
|
72
|
+
`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID = `$historyId
|
|
73
|
+
`$global:MCPEE_TELEMETRY_LAST_STAMP = `$stamp
|
|
74
|
+
return
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
`$success = if (`$?) { 'true' } else { 'false' }
|
|
78
|
+
`$nativeExit = if (`$null -eq `$LASTEXITCODE) { 0 } else { [int]`$LASTEXITCODE }
|
|
79
|
+
|
|
80
|
+
`$pyArgs = @()
|
|
81
|
+
if (`$py.Count -gt 1) {
|
|
82
|
+
`$pyArgs = `$py[1..(`$py.Count-1)]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
& `$py[0] @(`$pyArgs) `$scriptPath --session-id `$global:MCPEE_TELEMETRY_SESSION_ID --cwd `$PWD.Path --command `$commandTrim --success `$success --exit-code `$nativeExit *> `$null
|
|
86
|
+
|
|
87
|
+
`$global:MCPEE_TELEMETRY_LAST_HISTORY_ID = `$historyId
|
|
88
|
+
`$global:MCPEE_TELEMETRY_LAST_STAMP = `$stamp
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
# Hook de telemetria best-effort: nunca romper la consola.
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (-not (Test-Path Function:\global:MCPEE_ORIGINAL_PROMPT)) {
|
|
96
|
+
`$originalPromptBody = if (Test-Path Function:\global:prompt) { (Get-Command prompt -CommandType Function).ScriptBlock.ToString() } else { '"PS " + `$executionContext.SessionState.Path.CurrentLocation + "> "' }
|
|
97
|
+
Set-Item -Path Function:\global:MCPEE_ORIGINAL_PROMPT -Value ([ScriptBlock]::Create(`$originalPromptBody)) -Force
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function global:prompt {
|
|
101
|
+
Invoke-McpeeTerminalTelemetry
|
|
102
|
+
& global:MCPEE_ORIGINAL_PROMPT
|
|
103
|
+
}
|
|
104
|
+
# <<< MCPEE_TERMINAL_TELEMETRY_END <<<
|
|
105
|
+
"@
|
|
106
|
+
|
|
107
|
+
function Install-InProfile {
|
|
108
|
+
param([string]$ProfilePath)
|
|
109
|
+
|
|
110
|
+
$dir = Split-Path $ProfilePath -Parent
|
|
111
|
+
if (-not (Test-Path $dir)) {
|
|
112
|
+
New-Item -ItemType Directory -Path $dir -Force | Out-Null
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (-not (Test-Path $ProfilePath)) {
|
|
116
|
+
New-Item -ItemType File -Path $ProfilePath -Force | Out-Null
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
$content = Get-Content -Path $ProfilePath -Raw -ErrorAction SilentlyContinue
|
|
120
|
+
if ($null -eq $content) { $content = '' }
|
|
121
|
+
|
|
122
|
+
if ($content -match 'MCPEE_TERMINAL_TELEMETRY_START') {
|
|
123
|
+
$updated = [regex]::Replace(
|
|
124
|
+
$content,
|
|
125
|
+
'(?s)# >>> MCPEE_TERMINAL_TELEMETRY_START >>>.*?# <<< MCPEE_TERMINAL_TELEMETRY_END <<<',
|
|
126
|
+
$hookBlock
|
|
127
|
+
)
|
|
128
|
+
Set-Content -Path $ProfilePath -Value $updated -Encoding UTF8
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
if (-not [string]::IsNullOrWhiteSpace($content) -and -not $content.EndsWith("`n")) {
|
|
132
|
+
$content += "`r`n"
|
|
133
|
+
}
|
|
134
|
+
$content += "`r`n$hookBlock`r`n"
|
|
135
|
+
Set-Content -Path $ProfilePath -Value $content -Encoding UTF8
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
Write-Host "[ok] Hook instalado/actualizado en: $ProfilePath"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if ($CurrentProcess) {
|
|
142
|
+
Invoke-Expression $hookBlock
|
|
143
|
+
Write-Host '[ok] Hook cargado en la sesion actual.'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
Install-InProfile -ProfilePath $PROFILE.CurrentUserCurrentHost
|
|
147
|
+
if ($AllHosts) {
|
|
148
|
+
Install-InProfile -ProfilePath $PROFILE.CurrentUserAllHosts
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
Write-Host '[ok] Telemetria global de terminal activada (best-effort).'
|
|
152
|
+
Write-Host '[hint] Para desinstalar usa: mcpee observe-off'
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[string]$SourceConnectionString = 'Server=(localdb)\MSSQLLocalDB;Database=TechRidersDev;Trusted_Connection=True;MultipleActiveResultSets=True;TrustServerCertificate=True',
|
|
3
|
+
[string]$TargetServer = 'sql-techriders-dev-ohbnpr4l4fihc.database.windows.net',
|
|
4
|
+
[string]$TargetDatabase = 'sqldb-techriders-dev',
|
|
5
|
+
[string]$TargetUser = 'sqladminuser'
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
$ErrorActionPreference = 'Stop'
|
|
9
|
+
Add-Type -AssemblyName System.Data
|
|
10
|
+
|
|
11
|
+
if ([string]::IsNullOrWhiteSpace($env:SQL_ADMIN_PASSWORD)) {
|
|
12
|
+
throw 'Missing SQL_ADMIN_PASSWORD environment variable.'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
$targetConnectionString = "Server=tcp:$TargetServer,1433;Initial Catalog=$TargetDatabase;Persist Security Info=False;User ID=$TargetUser;Password=$($env:SQL_ADMIN_PASSWORD);MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;"
|
|
16
|
+
|
|
17
|
+
$sourceConn = New-Object System.Data.SqlClient.SqlConnection $SourceConnectionString
|
|
18
|
+
$targetConn = New-Object System.Data.SqlClient.SqlConnection $targetConnectionString
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
$sourceConn.Open()
|
|
22
|
+
$targetConn.Open()
|
|
23
|
+
|
|
24
|
+
$tableCmd = $sourceConn.CreateCommand()
|
|
25
|
+
$tableCmd.CommandText = @"
|
|
26
|
+
SELECT s.name AS SchemaName, t.name AS TableName
|
|
27
|
+
FROM sys.tables t
|
|
28
|
+
JOIN sys.schemas s ON s.schema_id = t.schema_id
|
|
29
|
+
WHERE t.is_ms_shipped = 0
|
|
30
|
+
ORDER BY s.name, t.name;
|
|
31
|
+
"@
|
|
32
|
+
|
|
33
|
+
$reader = $tableCmd.ExecuteReader()
|
|
34
|
+
$tables = New-Object System.Collections.Generic.List[object]
|
|
35
|
+
while ($reader.Read()) {
|
|
36
|
+
$schema = $reader.GetString(0)
|
|
37
|
+
$table = $reader.GetString(1)
|
|
38
|
+
if ($table -ne '__EFMigrationsHistory') {
|
|
39
|
+
$tables.Add([PSCustomObject]@{ Schema = $schema; Table = $table })
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
$reader.Close()
|
|
43
|
+
|
|
44
|
+
if ($tables.Count -eq 0) {
|
|
45
|
+
throw 'No source user tables found.'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
foreach ($t in $tables) {
|
|
49
|
+
$fullName = "[$($t.Schema)].[$($t.Table)]"
|
|
50
|
+
$disableCmd = $targetConn.CreateCommand()
|
|
51
|
+
$disableCmd.CommandText = "ALTER TABLE $fullName NOCHECK CONSTRAINT ALL;"
|
|
52
|
+
[void]$disableCmd.ExecuteNonQuery()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
$summary = New-Object System.Collections.Generic.List[object]
|
|
56
|
+
|
|
57
|
+
foreach ($t in $tables) {
|
|
58
|
+
$fullName = "[$($t.Schema)].[$($t.Table)]"
|
|
59
|
+
|
|
60
|
+
$deleteCmd = $targetConn.CreateCommand()
|
|
61
|
+
$deleteCmd.CommandText = "DELETE FROM $fullName;"
|
|
62
|
+
[void]$deleteCmd.ExecuteNonQuery()
|
|
63
|
+
|
|
64
|
+
$selectCmd = $sourceConn.CreateCommand()
|
|
65
|
+
$selectCmd.CommandText = "SELECT * FROM $fullName;"
|
|
66
|
+
|
|
67
|
+
$adapter = New-Object System.Data.SqlClient.SqlDataAdapter $selectCmd
|
|
68
|
+
$dataTable = New-Object System.Data.DataTable
|
|
69
|
+
[void]$adapter.Fill($dataTable)
|
|
70
|
+
|
|
71
|
+
if ($dataTable.Rows.Count -gt 0) {
|
|
72
|
+
$bulkCopy = New-Object System.Data.SqlClient.SqlBulkCopy($targetConn, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity, $null)
|
|
73
|
+
$bulkCopy.DestinationTableName = $fullName
|
|
74
|
+
$bulkCopy.BulkCopyTimeout = 0
|
|
75
|
+
foreach ($col in $dataTable.Columns) {
|
|
76
|
+
[void]$bulkCopy.ColumnMappings.Add($col.ColumnName, $col.ColumnName)
|
|
77
|
+
}
|
|
78
|
+
$bulkCopy.WriteToServer($dataTable)
|
|
79
|
+
$bulkCopy.Close()
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
$summary.Add([PSCustomObject]@{ Table = $fullName; Rows = $dataTable.Rows.Count })
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
foreach ($t in $tables) {
|
|
86
|
+
$fullName = "[$($t.Schema)].[$($t.Table)]"
|
|
87
|
+
$enableCmd = $targetConn.CreateCommand()
|
|
88
|
+
$enableCmd.CommandText = "ALTER TABLE $fullName WITH CHECK CHECK CONSTRAINT ALL;"
|
|
89
|
+
[void]$enableCmd.ExecuteNonQuery()
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
$summary | Sort-Object Table | Format-Table -AutoSize | Out-String | Write-Output
|
|
93
|
+
Write-Output 'MIGRATION_COMPLETED'
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
if ($sourceConn.State -eq 'Open') { $sourceConn.Close() }
|
|
97
|
+
if ($targetConn.State -eq 'Open') { $targetConn.Close() }
|
|
98
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$DryRun
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
$ErrorActionPreference = 'Stop'
|
|
6
|
+
|
|
7
|
+
function Resolve-RepoRoot {
|
|
8
|
+
return (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Has-CodegraphIndex([string]$path) {
|
|
12
|
+
return (Test-Path (Join-Path $path '.codegraph'))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Resolve-TargetPath {
|
|
16
|
+
param([string]$RepoRoot)
|
|
17
|
+
|
|
18
|
+
$envPath = [Environment]::GetEnvironmentVariable('CODEGRAPH_PROJECT_PATH')
|
|
19
|
+
if (-not [string]::IsNullOrWhiteSpace($envPath)) {
|
|
20
|
+
$resolvedEnvPath = $null
|
|
21
|
+
try {
|
|
22
|
+
$resolvedEnvPath = (Resolve-Path $envPath).Path
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
$resolvedEnvPath = $null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if ($resolvedEnvPath -and (Has-CodegraphIndex $resolvedEnvPath)) {
|
|
29
|
+
return $resolvedEnvPath
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$projectCandidates = @()
|
|
34
|
+
$projectsRoot = Join-Path $RepoRoot 'projects'
|
|
35
|
+
if (Test-Path $projectsRoot) {
|
|
36
|
+
foreach ($projectDir in Get-ChildItem -Path $projectsRoot -Directory -ErrorAction SilentlyContinue) {
|
|
37
|
+
if (Has-CodegraphIndex $projectDir.FullName) {
|
|
38
|
+
$projectCandidates += $projectDir.FullName
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ($projectCandidates.Count -eq 1) {
|
|
44
|
+
return $projectCandidates[0]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (Has-CodegraphIndex $RepoRoot) {
|
|
48
|
+
return $RepoRoot
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if ($projectCandidates.Count -gt 1) {
|
|
52
|
+
return $projectCandidates[0]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return $RepoRoot
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
$repoRoot = Resolve-RepoRoot
|
|
59
|
+
$targetPath = Resolve-TargetPath -RepoRoot $repoRoot
|
|
60
|
+
|
|
61
|
+
if ($DryRun) {
|
|
62
|
+
Write-Output $targetPath
|
|
63
|
+
exit 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
& codegraph serve --mcp --path $targetPath
|
|
67
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$DryRun
|
|
3
|
+
)
|
|
4
|
+
|
|
5
|
+
$ErrorActionPreference = 'Stop'
|
|
6
|
+
|
|
7
|
+
function Resolve-RepoRoot {
|
|
8
|
+
return (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function Is-GitRepo([string]$path) {
|
|
12
|
+
return (Test-Path (Join-Path $path '.git'))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function Has-GitNexusIndex([string]$path) {
|
|
16
|
+
return (Test-Path (Join-Path $path '.gitnexus'))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function Resolve-TargetPath {
|
|
20
|
+
param([string]$RepoRoot)
|
|
21
|
+
|
|
22
|
+
$envPath = [Environment]::GetEnvironmentVariable('GITNEXUS_PROJECT_PATH')
|
|
23
|
+
if (-not [string]::IsNullOrWhiteSpace($envPath)) {
|
|
24
|
+
$resolvedEnvPath = $null
|
|
25
|
+
try {
|
|
26
|
+
$resolvedEnvPath = (Resolve-Path $envPath).Path
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
$resolvedEnvPath = $null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if ($resolvedEnvPath -and (Is-GitRepo $resolvedEnvPath)) {
|
|
33
|
+
return $resolvedEnvPath
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$projectCandidates = @()
|
|
38
|
+
$indexedProjectCandidates = @()
|
|
39
|
+
$projectsRoot = Join-Path $RepoRoot 'projects'
|
|
40
|
+
|
|
41
|
+
if (Test-Path $projectsRoot) {
|
|
42
|
+
foreach ($projectDir in Get-ChildItem -Path $projectsRoot -Directory -ErrorAction SilentlyContinue) {
|
|
43
|
+
if (Is-GitRepo $projectDir.FullName) {
|
|
44
|
+
$projectCandidates += $projectDir.FullName
|
|
45
|
+
if (Has-GitNexusIndex $projectDir.FullName) {
|
|
46
|
+
$indexedProjectCandidates += $projectDir.FullName
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if ($indexedProjectCandidates.Count -eq 1) {
|
|
53
|
+
return $indexedProjectCandidates[0]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if ((Has-GitNexusIndex $RepoRoot) -and (Is-GitRepo $RepoRoot)) {
|
|
57
|
+
return $RepoRoot
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if ($indexedProjectCandidates.Count -gt 1) {
|
|
61
|
+
return $indexedProjectCandidates[0]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if ($projectCandidates.Count -eq 1) {
|
|
65
|
+
return $projectCandidates[0]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if ($projectCandidates.Count -gt 1) {
|
|
69
|
+
return $projectCandidates[0]
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return $RepoRoot
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
$repoRoot = Resolve-RepoRoot
|
|
76
|
+
$targetPath = Resolve-TargetPath -RepoRoot $repoRoot
|
|
77
|
+
|
|
78
|
+
if ($DryRun) {
|
|
79
|
+
Write-Output $targetPath
|
|
80
|
+
exit 0
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Set-Location $targetPath
|
|
84
|
+
& gitnexus mcp
|
|
85
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
param(
|
|
2
|
+
[switch]$AllHosts,
|
|
3
|
+
[switch]$CurrentProcess
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
Set-StrictMode -Version Latest
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
|
|
9
|
+
function Remove-FromProfile {
|
|
10
|
+
param([string]$ProfilePath)
|
|
11
|
+
|
|
12
|
+
if (-not (Test-Path $ProfilePath)) {
|
|
13
|
+
return
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
$content = Get-Content -Path $ProfilePath -Raw -ErrorAction SilentlyContinue
|
|
17
|
+
if ($null -eq $content) {
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if ($content -match 'MCPEE_TERMINAL_TELEMETRY_START') {
|
|
22
|
+
$updated = [regex]::Replace(
|
|
23
|
+
$content,
|
|
24
|
+
'(?s)\r?\n?# >>> MCPEE_TERMINAL_TELEMETRY_START >>>.*?# <<< MCPEE_TERMINAL_TELEMETRY_END <<<\r?\n?',
|
|
25
|
+
"`r`n"
|
|
26
|
+
)
|
|
27
|
+
Set-Content -Path $ProfilePath -Value $updated -Encoding UTF8
|
|
28
|
+
Write-Host "[ok] Hook eliminado de: $ProfilePath"
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if ($CurrentProcess) {
|
|
33
|
+
Remove-Item Function:\global:prompt -ErrorAction SilentlyContinue
|
|
34
|
+
if (Test-Path Function:\global:MCPEE_ORIGINAL_PROMPT) {
|
|
35
|
+
Set-Item -Path Function:\global:prompt -Value ((Get-Command MCPEE_ORIGINAL_PROMPT -CommandType Function).ScriptBlock) -Force
|
|
36
|
+
Remove-Item Function:\global:MCPEE_ORIGINAL_PROMPT -ErrorAction SilentlyContinue
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Remove-Item Function:\global:Invoke-McpeeTerminalTelemetry -ErrorAction SilentlyContinue
|
|
40
|
+
Remove-Item Function:\global:Get-McpeeTelemetryPythonCommand -ErrorAction SilentlyContinue
|
|
41
|
+
Remove-Variable MCPEE_TELEMETRY_REPO_ROOT -Scope Global -ErrorAction SilentlyContinue
|
|
42
|
+
Remove-Variable MCPEE_TELEMETRY_SESSION_ID -Scope Global -ErrorAction SilentlyContinue
|
|
43
|
+
Remove-Variable MCPEE_TELEMETRY_LAST_HISTORY_ID -Scope Global -ErrorAction SilentlyContinue
|
|
44
|
+
Remove-Variable MCPEE_TELEMETRY_LAST_STAMP -Scope Global -ErrorAction SilentlyContinue
|
|
45
|
+
Write-Host '[ok] Hook desactivado en la sesion actual.'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Remove-FromProfile -ProfilePath $PROFILE.CurrentUserCurrentHost
|
|
49
|
+
if ($AllHosts) {
|
|
50
|
+
Remove-FromProfile -ProfilePath $PROFILE.CurrentUserAllHosts
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
Write-Host '[ok] Telemetria global de terminal desactivada.'
|
|
@@ -143,6 +143,27 @@ function Ensure-PythonModuleInstalled {
|
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
function Sync-GraphifyArtifactsFromScripts {
|
|
147
|
+
$scriptsGraphPath = 'scripts/graphify-out/graph.json'
|
|
148
|
+
$contextGraphDir = 'context/graphify-out'
|
|
149
|
+
$contextGraphPath = Join-Path $contextGraphDir 'graph.json'
|
|
150
|
+
|
|
151
|
+
if (-not (Test-Path $scriptsGraphPath)) {
|
|
152
|
+
return $false
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
New-Item -ItemType Directory -Path $contextGraphDir -Force | Out-Null
|
|
156
|
+
Copy-Item -Path $scriptsGraphPath -Destination $contextGraphPath -Force
|
|
157
|
+
|
|
158
|
+
$scriptsManifestPath = 'scripts/graphify-out/manifest.json'
|
|
159
|
+
$contextManifestPath = Join-Path $contextGraphDir 'manifest.json'
|
|
160
|
+
if (Test-Path $scriptsManifestPath) {
|
|
161
|
+
Copy-Item -Path $scriptsManifestPath -Destination $contextManifestPath -Force
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return $true
|
|
165
|
+
}
|
|
166
|
+
|
|
146
167
|
Write-Host '== MCP platform prerequisites setup =='
|
|
147
168
|
|
|
148
169
|
$toolingManifest = Get-ToolingManifest -Path $toolingManifestPath
|
|
@@ -212,8 +233,15 @@ if (-not $SkipGraphify) {
|
|
|
212
233
|
}
|
|
213
234
|
|
|
214
235
|
if (-not (Test-Path 'context/graphify-out/graph.json')) {
|
|
215
|
-
Write-Host '[setup] graphify
|
|
216
|
-
& $pyCmd @pyArgs -m graphify
|
|
236
|
+
Write-Host '[setup] graphify update scripts --no-cluster'
|
|
237
|
+
& $pyCmd @pyArgs -m graphify update scripts --no-cluster
|
|
238
|
+
if ($LASTEXITCODE -ne 0) {
|
|
239
|
+
throw "graphify update failed with exit code $LASTEXITCODE"
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (Sync-GraphifyArtifactsFromScripts) {
|
|
243
|
+
Write-Host '[ok] graphify output synced from scripts/graphify-out to context/graphify-out'
|
|
244
|
+
}
|
|
217
245
|
}
|
|
218
246
|
else {
|
|
219
247
|
Write-Host '[ok] context/graphify-out/graph.json already exists'
|
|
@@ -69,6 +69,24 @@ 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
|
+
|
|
72
90
|
# Ensure report directory exists
|
|
73
91
|
$reportDir = Split-Path -Parent $setupValidationReportPath
|
|
74
92
|
if (-not (Test-Path $reportDir)) {
|
|
@@ -192,8 +210,9 @@ if ($toolingManifest -and ($toolingManifest.PSObject.Properties.Name -contains '
|
|
|
192
210
|
|
|
193
211
|
try {
|
|
194
212
|
$mcpRaw = Get-Content -Raw -Path '.vscode/mcp.json' | ConvertFrom-Json -Depth 20
|
|
195
|
-
|
|
196
|
-
|
|
213
|
+
$gitnexusCommand = [string]$mcpRaw.servers.gitnexus.command
|
|
214
|
+
if ($gitnexusCommand -eq 'npx') {
|
|
215
|
+
$errors += 'MCP gitnexus command should avoid `npx` (startup latency). Use local `gitnexus` or local wrapper script.'
|
|
197
216
|
}
|
|
198
217
|
$scanLimit = 0
|
|
199
218
|
$gitnexusHasEnv = ($mcpRaw.servers.gitnexus.PSObject.Properties.Name -contains 'env')
|
|
@@ -215,6 +234,8 @@ catch {
|
|
|
215
234
|
$errors += "Unable to parse .vscode/mcp.json: $($_.Exception.Message)"
|
|
216
235
|
}
|
|
217
236
|
|
|
237
|
+
Sync-GraphifyArtifactsIfNeeded
|
|
238
|
+
|
|
218
239
|
$specFiles = @(
|
|
219
240
|
"specs/architecture.spec.md",
|
|
220
241
|
"specs/azure-rag.spec.md",
|
|
@@ -332,9 +353,9 @@ else {
|
|
|
332
353
|
}
|
|
333
354
|
}
|
|
334
355
|
|
|
335
|
-
if (-not (Test-Path "context/graphify-out/graph.json")) {
|
|
356
|
+
if ((-not (Test-Path "context/graphify-out/graph.json")) -and (-not (Test-Path "scripts/graphify-out/graph.json"))) {
|
|
336
357
|
if (-not ($CIMode)) {
|
|
337
|
-
$errors += "Missing
|
|
358
|
+
$errors += "Missing graphify graph output. Run: py -3.14 -m graphify update scripts --no-cluster"
|
|
338
359
|
}
|
|
339
360
|
}
|
|
340
361
|
|