mcp-efficiency-engine 0.1.5 → 0.1.6
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/README.md +11 -0
- 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/.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/README.md
CHANGED
|
@@ -233,6 +233,17 @@ py -3 .\scripts\intake\run-routing-evals.py
|
|
|
233
233
|
.\scripts\ops\bye.ps1
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
+
Telemetría de terminal (PowerShell, opcional):
|
|
237
|
+
|
|
238
|
+
```powershell
|
|
239
|
+
mcpee observe-on
|
|
240
|
+
# ... comandos interactivos ...
|
|
241
|
+
mcpee observe-off
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
- `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`.
|
|
245
|
+
- `mcpee observe-off` elimina el hook global y restaura el perfil sin instrumentación.
|
|
246
|
+
|
|
236
247
|
Validación extendida recomendada:
|
|
237
248
|
|
|
238
249
|
```powershell
|
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.'
|