igel-qe-core 1.0.8 → 1.0.10

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,34 @@ program
15
15
  .description("IGEL QE Developer Experience CLI")
16
16
  .version("1.0.0");
17
17
  // ── helpers ────────────────────────────────────────────────────────────────────
18
- function getPythonBin() {
19
- const venv = join(PROJECT_ROOT, ".venv", "bin", "python");
20
- return existsSync(venv) ? venv : "python3";
18
+ function getSystemPython() {
19
+ for (const cmd of ["python3", "python", "py"]) {
20
+ try {
21
+ const res = spawnSync(cmd, ["-c", "import sys; print(sys.version)"], { encoding: "utf-8" });
22
+ if (!res.error && res.status === 0 && res.stdout.includes("3.")) {
23
+ return cmd;
24
+ }
25
+ }
26
+ catch {
27
+ // Continue
28
+ }
29
+ }
30
+ return "python3";
31
+ }
32
+ function getPythonBin(workspaceRoot) {
33
+ const root = workspaceRoot || process.cwd();
34
+ const isWin = process.platform === "win32";
35
+ const localVenv = isWin
36
+ ? join(root, ".venv", "Scripts", "python.exe")
37
+ : join(root, ".venv", "bin", "python");
38
+ if (existsSync(localVenv))
39
+ return localVenv;
40
+ const globalVenv = isWin
41
+ ? join(PROJECT_ROOT, ".venv", "Scripts", "python.exe")
42
+ : join(PROJECT_ROOT, ".venv", "bin", "python");
43
+ if (existsSync(globalVenv))
44
+ return globalVenv;
45
+ return getSystemPython();
21
46
  }
22
47
  function resolveRequirementsPath() {
23
48
  const primary = join(PROJECT_ROOT, "requirements.txt");
@@ -196,11 +221,11 @@ function listFrameworkContextFiles(workspaceRoot) {
196
221
  return Array.from(new Set(found)).sort();
197
222
  }
198
223
  function isCopilotCliAvailable() {
199
- const check = spawnSync("copilot", ["--version"], { encoding: "utf-8" });
224
+ const check = spawnSync("copilot", ["--version"], { encoding: "utf-8", shell: process.platform === "win32" });
200
225
  return check.status === 0;
201
226
  }
202
227
  function isCopilotAuthenticated(workspaceRoot) {
203
- const probe = spawnSync("copilot", ["-C", workspaceRoot, "-p", "Reply with exactly OK.", "--allow-all-tools", "--no-color", "-s"], { encoding: "utf-8" });
228
+ 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
229
  return probe.status === 0;
205
230
  }
206
231
  function ensureCopilotLogin(workspaceRoot) {
@@ -210,7 +235,7 @@ function ensureCopilotLogin(workspaceRoot) {
210
235
  return true;
211
236
  console.log(chalk.yellow(" ⚠ GitHub Copilot CLI is not authenticated."));
212
237
  console.log(chalk.yellow(" → Please complete Copilot login to continue context enrichment."));
213
- const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8" });
238
+ const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8", shell: process.platform === "win32" });
214
239
  if (login.status !== 0)
215
240
  return false;
216
241
  return isCopilotAuthenticated(workspaceRoot);
@@ -237,7 +262,7 @@ function enrichWithCopilotCli(workspaceRoot, contextFiles) {
237
262
  "--allow-all-paths",
238
263
  "--no-color",
239
264
  "-s",
240
- ], { encoding: "utf-8" });
265
+ ], { encoding: "utf-8", shell: process.platform === "win32" });
241
266
  if (result.status === 0) {
242
267
  updated += 1;
243
268
  }
@@ -308,7 +333,8 @@ function scaffoldGithubAgents(targetDir) {
308
333
  */
309
334
  function runWorkflow(action, args) {
310
335
  return new Promise((resolve, reject) => {
311
- const py = getPythonBin();
336
+ const workspaceRoot = process.cwd();
337
+ const py = getPythonBin(workspaceRoot);
312
338
  const proc = spawn(py, ["-m", "src.cli.workflow_cli", "--action", action, "--args", JSON.stringify(args)], { cwd: PROJECT_ROOT, env: process.env, stdio: ["ignore", "pipe", "pipe"] });
313
339
  let stdout = "";
314
340
  let stderr = "";
@@ -341,11 +367,11 @@ program
341
367
  const workspaceRoot = process.cwd();
342
368
  console.log(chalk.blue("\nInitializing IGEL QE AI Platform…\n"));
343
369
  // Step 1 — venv + pip
344
- const venvPath = join(PROJECT_ROOT, ".venv");
370
+ const venvPath = join(workspaceRoot, ".venv");
345
371
  if (!existsSync(venvPath)) {
346
- console.log(chalk.yellow("1. Creating virtual environment…"));
372
+ console.log(chalk.yellow("1. Creating virtual environment in workspace…"));
347
373
  try {
348
- execSync(`python3 -m venv .venv`, { cwd: PROJECT_ROOT, stdio: "inherit" });
374
+ execSync(`${getSystemPython()} -m venv .venv`, { cwd: workspaceRoot, stdio: "inherit" });
349
375
  console.log(chalk.green(" ✓ venv created"));
350
376
  }
351
377
  catch (e) {
@@ -362,8 +388,8 @@ program
362
388
  }
363
389
  else {
364
390
  try {
365
- execSync(`${getPythonBin()} -m pip install -q -r \"${reqPath}\"`, {
366
- cwd: PROJECT_ROOT, stdio: "inherit",
391
+ execSync(`${getPythonBin(workspaceRoot)} -m pip install -q -r \"${reqPath}\"`, {
392
+ cwd: workspaceRoot, stdio: "inherit",
367
393
  });
368
394
  console.log(chalk.green(" ✓ Dependencies installed"));
369
395
  }
@@ -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.10",
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,35 @@ program
21
21
 
22
22
  // ── helpers ────────────────────────────────────────────────────────────────────
23
23
 
24
- function getPythonBin(): string {
25
- const venv = join(PROJECT_ROOT, ".venv", "bin", "python");
26
- return existsSync(venv) ? venv : "python3";
24
+ function getSystemPython(): string {
25
+ for (const cmd of ["python3", "python", "py"]) {
26
+ try {
27
+ const res = spawnSync(cmd, ["-c", "import sys; print(sys.version)"], { encoding: "utf-8" });
28
+ if (!res.error && res.status === 0 && res.stdout.includes("3.")) {
29
+ return cmd;
30
+ }
31
+ } catch {
32
+ // Continue
33
+ }
34
+ }
35
+ return "python3";
36
+ }
37
+
38
+ function getPythonBin(workspaceRoot?: string): string {
39
+ const root = workspaceRoot || process.cwd();
40
+ const isWin = process.platform === "win32";
41
+
42
+ const localVenv = isWin
43
+ ? join(root, ".venv", "Scripts", "python.exe")
44
+ : join(root, ".venv", "bin", "python");
45
+ if (existsSync(localVenv)) return localVenv;
46
+
47
+ const globalVenv = isWin
48
+ ? join(PROJECT_ROOT, ".venv", "Scripts", "python.exe")
49
+ : join(PROJECT_ROOT, ".venv", "bin", "python");
50
+ if (existsSync(globalVenv)) return globalVenv;
51
+
52
+ return getSystemPython();
27
53
  }
28
54
 
29
55
  function resolveRequirementsPath(): string | null {
@@ -206,7 +232,7 @@ function listFrameworkContextFiles(workspaceRoot: string): string[] {
206
232
  }
207
233
 
208
234
  function isCopilotCliAvailable(): boolean {
209
- const check = spawnSync("copilot", ["--version"], { encoding: "utf-8" });
235
+ const check = spawnSync("copilot", ["--version"], { encoding: "utf-8", shell: process.platform === "win32" });
210
236
  return check.status === 0;
211
237
  }
212
238
 
@@ -214,7 +240,7 @@ function isCopilotAuthenticated(workspaceRoot: string): boolean {
214
240
  const probe = spawnSync(
215
241
  "copilot",
216
242
  ["-C", workspaceRoot, "-p", "Reply with exactly OK.", "--allow-all-tools", "--no-color", "-s"],
217
- { encoding: "utf-8" },
243
+ { encoding: "utf-8", shell: process.platform === "win32" },
218
244
  );
219
245
  return probe.status === 0;
220
246
  }
@@ -226,7 +252,7 @@ function ensureCopilotLogin(workspaceRoot: string): boolean {
226
252
  console.log(chalk.yellow(" ⚠ GitHub Copilot CLI is not authenticated."));
227
253
  console.log(chalk.yellow(" → Please complete Copilot login to continue context enrichment."));
228
254
 
229
- const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8" });
255
+ const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8", shell: process.platform === "win32" });
230
256
  if (login.status !== 0) return false;
231
257
  return isCopilotAuthenticated(workspaceRoot);
232
258
  }
@@ -258,7 +284,7 @@ function enrichWithCopilotCli(workspaceRoot: string, contextFiles: string[]): {
258
284
  "--no-color",
259
285
  "-s",
260
286
  ],
261
- { encoding: "utf-8" },
287
+ { encoding: "utf-8", shell: process.platform === "win32" },
262
288
  );
263
289
 
264
290
  if (result.status === 0) {
@@ -334,7 +360,8 @@ function scaffoldGithubAgents(targetDir: string): string[] {
334
360
  */
335
361
  function runWorkflow(action: string, args: Record<string, unknown>): Promise<unknown> {
336
362
  return new Promise((resolve, reject) => {
337
- const py = getPythonBin();
363
+ const workspaceRoot = process.cwd();
364
+ const py = getPythonBin(workspaceRoot);
338
365
  const proc = spawn(
339
366
  py,
340
367
  ["-m", "src.cli.workflow_cli", "--action", action, "--args", JSON.stringify(args)],
@@ -372,11 +399,11 @@ program
372
399
  console.log(chalk.blue("\nInitializing IGEL QE AI Platform…\n"));
373
400
 
374
401
  // Step 1 — venv + pip
375
- const venvPath = join(PROJECT_ROOT, ".venv");
402
+ const venvPath = join(workspaceRoot, ".venv");
376
403
  if (!existsSync(venvPath)) {
377
- console.log(chalk.yellow("1. Creating virtual environment…"));
404
+ console.log(chalk.yellow("1. Creating virtual environment in workspace…"));
378
405
  try {
379
- execSync(`python3 -m venv .venv`, { cwd: PROJECT_ROOT, stdio: "inherit" });
406
+ execSync(`${getSystemPython()} -m venv .venv`, { cwd: workspaceRoot, stdio: "inherit" });
380
407
  console.log(chalk.green(" ✓ venv created"));
381
408
  } catch (e: unknown) {
382
409
  console.log(chalk.yellow(` ⚠ venv setup skipped: ${e instanceof Error ? e.message : String(e)}`));
@@ -391,8 +418,8 @@ program
391
418
  console.log(chalk.yellow(" ⚠ requirements file not found; skipping Python dependency install"));
392
419
  } else {
393
420
  try {
394
- execSync(`${getPythonBin()} -m pip install -q -r \"${reqPath}\"`, {
395
- cwd: PROJECT_ROOT, stdio: "inherit",
421
+ execSync(`${getPythonBin(workspaceRoot)} -m pip install -q -r \"${reqPath}\"`, {
422
+ cwd: workspaceRoot, stdio: "inherit",
396
423
  });
397
424
  console.log(chalk.green(" ✓ Dependencies installed"));
398
425
  } catch (e: unknown) {
@@ -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