igel-qe-core 1.0.8 → 1.0.9

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/dist/cli/index.js CHANGED
@@ -15,9 +15,26 @@ program
15
15
  .description("IGEL QE Developer Experience CLI")
16
16
  .version("1.0.0");
17
17
  // ── helpers ────────────────────────────────────────────────────────────────────
18
+ function getSystemPython() {
19
+ for (const cmd of ["python3", "python", "py"]) {
20
+ try {
21
+ const res = spawnSync(cmd, ["--version"], { stdio: "ignore" });
22
+ if (res.status === 0 || res.status === null) {
23
+ return cmd;
24
+ }
25
+ }
26
+ catch {
27
+ // Continue
28
+ }
29
+ }
30
+ return "python3";
31
+ }
18
32
  function getPythonBin() {
19
- const venv = join(PROJECT_ROOT, ".venv", "bin", "python");
20
- return existsSync(venv) ? venv : "python3";
33
+ const isWin = process.platform === "win32";
34
+ const venv = isWin
35
+ ? join(PROJECT_ROOT, ".venv", "Scripts", "python.exe")
36
+ : join(PROJECT_ROOT, ".venv", "bin", "python");
37
+ return existsSync(venv) ? venv : getSystemPython();
21
38
  }
22
39
  function resolveRequirementsPath() {
23
40
  const primary = join(PROJECT_ROOT, "requirements.txt");
@@ -196,11 +213,11 @@ function listFrameworkContextFiles(workspaceRoot) {
196
213
  return Array.from(new Set(found)).sort();
197
214
  }
198
215
  function isCopilotCliAvailable() {
199
- const check = spawnSync("copilot", ["--version"], { encoding: "utf-8" });
216
+ const check = spawnSync("copilot", ["--version"], { encoding: "utf-8", shell: process.platform === "win32" });
200
217
  return check.status === 0;
201
218
  }
202
219
  function isCopilotAuthenticated(workspaceRoot) {
203
- const probe = spawnSync("copilot", ["-C", workspaceRoot, "-p", "Reply with exactly OK.", "--allow-all-tools", "--no-color", "-s"], { encoding: "utf-8" });
220
+ const probe = spawnSync("copilot", ["-C", workspaceRoot, "-p", "Reply with exactly OK.", "--allow-all-tools", "--no-color", "-s"], { encoding: "utf-8", shell: process.platform === "win32" });
204
221
  return probe.status === 0;
205
222
  }
206
223
  function ensureCopilotLogin(workspaceRoot) {
@@ -210,7 +227,7 @@ function ensureCopilotLogin(workspaceRoot) {
210
227
  return true;
211
228
  console.log(chalk.yellow(" ⚠ GitHub Copilot CLI is not authenticated."));
212
229
  console.log(chalk.yellow(" → Please complete Copilot login to continue context enrichment."));
213
- const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8" });
230
+ const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8", shell: process.platform === "win32" });
214
231
  if (login.status !== 0)
215
232
  return false;
216
233
  return isCopilotAuthenticated(workspaceRoot);
@@ -237,7 +254,7 @@ function enrichWithCopilotCli(workspaceRoot, contextFiles) {
237
254
  "--allow-all-paths",
238
255
  "--no-color",
239
256
  "-s",
240
- ], { encoding: "utf-8" });
257
+ ], { encoding: "utf-8", shell: process.platform === "win32" });
241
258
  if (result.status === 0) {
242
259
  updated += 1;
243
260
  }
@@ -345,7 +362,7 @@ program
345
362
  if (!existsSync(venvPath)) {
346
363
  console.log(chalk.yellow("1. Creating virtual environment…"));
347
364
  try {
348
- execSync(`python3 -m venv .venv`, { cwd: PROJECT_ROOT, stdio: "inherit" });
365
+ execSync(`${getSystemPython()} -m venv .venv`, { cwd: PROJECT_ROOT, stdio: "inherit" });
349
366
  console.log(chalk.green(" ✓ venv created"));
350
367
  }
351
368
  catch (e) {
@@ -3,7 +3,7 @@
3
3
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
6
- import { spawn } from "child_process";
6
+ import { spawn, spawnSync } from "child_process";
7
7
  import path from "path";
8
8
  import { fileURLToPath } from "url";
9
9
  import fs from "fs";
@@ -11,18 +11,33 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
11
11
  // Project root: dist/mcp/server.js -> ../../ = project root
12
12
  const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
13
13
  // Resolve the correct Python executable:
14
- // 1. Check for a local .venv with a Linux bin/ (created by python3 -m venv)
15
- // 2. Fall back to system python3 / python
14
+ // 1. Check for a local .venv (Linux or Windows path layouts)
15
+ // 2. Fall back to system python3 / python / py
16
16
  function resolvePythonExecutable() {
17
- const candidates = [
18
- path.join(PROJECT_ROOT, '.venv', 'bin', 'python'),
19
- path.join(PROJECT_ROOT, '.venv', 'bin', 'python3'),
20
- ];
17
+ const isWin = process.platform === 'win32';
18
+ const candidates = isWin
19
+ ? [
20
+ path.join(PROJECT_ROOT, '.venv', 'Scripts', 'python.exe'),
21
+ path.join(PROJECT_ROOT, '.venv', 'Scripts', 'python'),
22
+ ]
23
+ : [
24
+ path.join(PROJECT_ROOT, '.venv', 'bin', 'python'),
25
+ path.join(PROJECT_ROOT, '.venv', 'bin', 'python3'),
26
+ ];
21
27
  for (const c of candidates) {
22
28
  if (fs.existsSync(c))
23
29
  return c;
24
30
  }
25
- // Check system python3 / python
31
+ // Check system python3 / python / py
32
+ for (const cmd of ['python3', 'python', 'py']) {
33
+ try {
34
+ const res = spawnSync(cmd, ['--version'], { stdio: 'ignore' });
35
+ if (res.status === 0 || res.status === null) {
36
+ return cmd;
37
+ }
38
+ }
39
+ catch { }
40
+ }
26
41
  return 'python3';
27
42
  }
28
43
  const server = new Server({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "igel-qe-core",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "IGEL QE Developer Experience Layer (CLI & MCP)",
5
5
  "type": "module",
6
6
  "publishConfig": {
package/src/cli/index.ts CHANGED
@@ -21,9 +21,26 @@ program
21
21
 
22
22
  // ── helpers ────────────────────────────────────────────────────────────────────
23
23
 
24
+ function getSystemPython(): string {
25
+ for (const cmd of ["python3", "python", "py"]) {
26
+ try {
27
+ const res = spawnSync(cmd, ["--version"], { stdio: "ignore" });
28
+ if (res.status === 0 || res.status === null) {
29
+ return cmd;
30
+ }
31
+ } catch {
32
+ // Continue
33
+ }
34
+ }
35
+ return "python3";
36
+ }
37
+
24
38
  function getPythonBin(): string {
25
- const venv = join(PROJECT_ROOT, ".venv", "bin", "python");
26
- return existsSync(venv) ? venv : "python3";
39
+ const isWin = process.platform === "win32";
40
+ const venv = isWin
41
+ ? join(PROJECT_ROOT, ".venv", "Scripts", "python.exe")
42
+ : join(PROJECT_ROOT, ".venv", "bin", "python");
43
+ return existsSync(venv) ? venv : getSystemPython();
27
44
  }
28
45
 
29
46
  function resolveRequirementsPath(): string | null {
@@ -206,7 +223,7 @@ function listFrameworkContextFiles(workspaceRoot: string): string[] {
206
223
  }
207
224
 
208
225
  function isCopilotCliAvailable(): boolean {
209
- const check = spawnSync("copilot", ["--version"], { encoding: "utf-8" });
226
+ const check = spawnSync("copilot", ["--version"], { encoding: "utf-8", shell: process.platform === "win32" });
210
227
  return check.status === 0;
211
228
  }
212
229
 
@@ -214,7 +231,7 @@ function isCopilotAuthenticated(workspaceRoot: string): boolean {
214
231
  const probe = spawnSync(
215
232
  "copilot",
216
233
  ["-C", workspaceRoot, "-p", "Reply with exactly OK.", "--allow-all-tools", "--no-color", "-s"],
217
- { encoding: "utf-8" },
234
+ { encoding: "utf-8", shell: process.platform === "win32" },
218
235
  );
219
236
  return probe.status === 0;
220
237
  }
@@ -226,7 +243,7 @@ function ensureCopilotLogin(workspaceRoot: string): boolean {
226
243
  console.log(chalk.yellow(" ⚠ GitHub Copilot CLI is not authenticated."));
227
244
  console.log(chalk.yellow(" → Please complete Copilot login to continue context enrichment."));
228
245
 
229
- const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8" });
246
+ const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8", shell: process.platform === "win32" });
230
247
  if (login.status !== 0) return false;
231
248
  return isCopilotAuthenticated(workspaceRoot);
232
249
  }
@@ -258,7 +275,7 @@ function enrichWithCopilotCli(workspaceRoot: string, contextFiles: string[]): {
258
275
  "--no-color",
259
276
  "-s",
260
277
  ],
261
- { encoding: "utf-8" },
278
+ { encoding: "utf-8", shell: process.platform === "win32" },
262
279
  );
263
280
 
264
281
  if (result.status === 0) {
@@ -376,7 +393,7 @@ program
376
393
  if (!existsSync(venvPath)) {
377
394
  console.log(chalk.yellow("1. Creating virtual environment…"));
378
395
  try {
379
- execSync(`python3 -m venv .venv`, { cwd: PROJECT_ROOT, stdio: "inherit" });
396
+ execSync(`${getSystemPython()} -m venv .venv`, { cwd: PROJECT_ROOT, stdio: "inherit" });
380
397
  console.log(chalk.green(" ✓ venv created"));
381
398
  } catch (e: unknown) {
382
399
  console.log(chalk.yellow(` ⚠ venv setup skipped: ${e instanceof Error ? e.message : String(e)}`));
@@ -28,6 +28,7 @@ pydantic>=2.7.0
28
28
  eval_type_backport>=0.2.0; python_version < "3.10"
29
29
  tenacity==9.0.0
30
30
  rich==13.9.4
31
+ PyYAML>=6.0.1
31
32
 
32
33
  # OSS local reranker (CrossEncoder — free, runs on CPU/GPU)
33
34
  # Default model: BAAI/bge-reranker-v2-m3 (multilingual, best open-source for IGEL DE/EN)
package/src/mcp/server.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
5
  import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
6
- import { spawn } from "child_process";
6
+ import { spawn, spawnSync } from "child_process";
7
7
  import path from "path";
8
8
  import { fileURLToPath } from "url";
9
9
  import fs from "fs";
@@ -14,17 +14,31 @@ const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
14
  const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
15
15
 
16
16
  // Resolve the correct Python executable:
17
- // 1. Check for a local .venv with a Linux bin/ (created by python3 -m venv)
18
- // 2. Fall back to system python3 / python
17
+ // 1. Check for a local .venv (Linux or Windows path layouts)
18
+ // 2. Fall back to system python3 / python / py
19
19
  function resolvePythonExecutable(): string {
20
- const candidates = [
21
- path.join(PROJECT_ROOT, '.venv', 'bin', 'python'),
22
- path.join(PROJECT_ROOT, '.venv', 'bin', 'python3'),
23
- ];
20
+ const isWin = process.platform === 'win32';
21
+ const candidates = isWin
22
+ ? [
23
+ path.join(PROJECT_ROOT, '.venv', 'Scripts', 'python.exe'),
24
+ path.join(PROJECT_ROOT, '.venv', 'Scripts', 'python'),
25
+ ]
26
+ : [
27
+ path.join(PROJECT_ROOT, '.venv', 'bin', 'python'),
28
+ path.join(PROJECT_ROOT, '.venv', 'bin', 'python3'),
29
+ ];
24
30
  for (const c of candidates) {
25
31
  if (fs.existsSync(c)) return c;
26
32
  }
27
- // Check system python3 / python
33
+ // Check system python3 / python / py
34
+ for (const cmd of ['python3', 'python', 'py']) {
35
+ try {
36
+ const res = spawnSync(cmd, ['--version'], { stdio: 'ignore' });
37
+ if (res.status === 0 || res.status === null) {
38
+ return cmd;
39
+ }
40
+ } catch {}
41
+ }
28
42
  return 'python3';
29
43
  }
30
44