igel-qe-core 1.0.12 → 1.0.16
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 +78 -32
- package/package.json +2 -1
- package/src/cli/index.ts +82 -30
package/dist/cli/index.js
CHANGED
|
@@ -221,30 +221,51 @@ function listFrameworkContextFiles(workspaceRoot) {
|
|
|
221
221
|
return Array.from(new Set(found)).sort();
|
|
222
222
|
}
|
|
223
223
|
function isCopilotCliAvailable() {
|
|
224
|
-
|
|
225
|
-
|
|
224
|
+
// Use a short timeout so this never blocks if copilot hangs on startup.
|
|
225
|
+
const check = spawnSync("copilot", ["--version"], {
|
|
226
|
+
encoding: "utf-8",
|
|
227
|
+
shell: process.platform === "win32",
|
|
228
|
+
timeout: 10_000,
|
|
229
|
+
});
|
|
230
|
+
return check.status === 0 && !check.error;
|
|
226
231
|
}
|
|
227
|
-
function isCopilotAuthenticated(
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
function isCopilotAuthenticated() {
|
|
233
|
+
// Use a lightweight auth-status check instead of spawning a full agent session.
|
|
234
|
+
// "copilot auth status" exits 0 when authenticated, non-zero otherwise.
|
|
235
|
+
// Fallback: if the sub-command doesn't exist, treat as unauthenticated.
|
|
236
|
+
const probe = spawnSync("copilot", ["auth", "status"], {
|
|
237
|
+
encoding: "utf-8",
|
|
238
|
+
shell: process.platform === "win32",
|
|
239
|
+
timeout: 10_000, // 10 s max — simple status call should be instant
|
|
240
|
+
});
|
|
241
|
+
return probe.status === 0 && !probe.error;
|
|
230
242
|
}
|
|
231
243
|
function ensureCopilotLogin(workspaceRoot) {
|
|
232
244
|
if (!isCopilotCliAvailable())
|
|
233
245
|
return false;
|
|
234
|
-
if (isCopilotAuthenticated(
|
|
246
|
+
if (isCopilotAuthenticated())
|
|
235
247
|
return true;
|
|
236
248
|
console.log(chalk.yellow(" ⚠ GitHub Copilot CLI is not authenticated."));
|
|
237
249
|
console.log(chalk.yellow(" → Please complete Copilot login to continue context enrichment."));
|
|
238
250
|
const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8", shell: process.platform === "win32" });
|
|
239
251
|
if (login.status !== 0)
|
|
240
252
|
return false;
|
|
241
|
-
return isCopilotAuthenticated(
|
|
253
|
+
return isCopilotAuthenticated();
|
|
242
254
|
}
|
|
255
|
+
// Maximum number of context.md files to enrich per init run.
|
|
256
|
+
// Prevents extremely long waits on repos with many folders.
|
|
257
|
+
const MAX_ENRICHMENT_FILES = 15;
|
|
243
258
|
function enrichWithCopilotCli(workspaceRoot, contextFiles) {
|
|
244
259
|
let updated = 0;
|
|
245
260
|
let failed = 0;
|
|
246
|
-
|
|
247
|
-
|
|
261
|
+
// Limit the number of files processed in one init run.
|
|
262
|
+
const filesToProcess = contextFiles.slice(0, MAX_ENRICHMENT_FILES);
|
|
263
|
+
const total = filesToProcess.length;
|
|
264
|
+
for (let i = 0; i < filesToProcess.length; i++) {
|
|
265
|
+
const absPath = filesToProcess[i];
|
|
266
|
+
const relPath = absPath.replace(`${workspaceRoot}/`, "").replace(`${workspaceRoot}\\`, "");
|
|
267
|
+
// Show per-file progress so the user knows work is happening.
|
|
268
|
+
process.stdout.write(` [${i + 1}/${total}] Enriching ${relPath}…`);
|
|
248
269
|
const prompt = [
|
|
249
270
|
`Update the markdown file at path '${relPath}' in place.`,
|
|
250
271
|
"Goal: make it both human-understandable and LLM-ready for automation framework reasoning.",
|
|
@@ -262,11 +283,18 @@ function enrichWithCopilotCli(workspaceRoot, contextFiles) {
|
|
|
262
283
|
"--allow-all-paths",
|
|
263
284
|
"--no-color",
|
|
264
285
|
"-s",
|
|
265
|
-
], {
|
|
266
|
-
|
|
286
|
+
], {
|
|
287
|
+
encoding: "utf-8",
|
|
288
|
+
shell: process.platform === "win32",
|
|
289
|
+
timeout: 120_000, // 2 min per file — prevents indefinite hangs
|
|
290
|
+
input: "", // close stdin immediately
|
|
291
|
+
});
|
|
292
|
+
if (result.status === 0 && !result.error) {
|
|
293
|
+
process.stdout.write(" ✓\n");
|
|
267
294
|
updated += 1;
|
|
268
295
|
}
|
|
269
296
|
else {
|
|
297
|
+
process.stdout.write(" ✗\n");
|
|
270
298
|
failed += 1;
|
|
271
299
|
}
|
|
272
300
|
}
|
|
@@ -388,7 +416,13 @@ program
|
|
|
388
416
|
}
|
|
389
417
|
else {
|
|
390
418
|
try {
|
|
391
|
-
|
|
419
|
+
const pyBin = getPythonBin(workspaceRoot);
|
|
420
|
+
// Wrap both the python path and requirements path in quotes so
|
|
421
|
+
// Windows paths that contain spaces (e.g. "IGEL Automation July 2026")
|
|
422
|
+
// are treated as a single argument by the shell.
|
|
423
|
+
// --prefer-binary: use pre-built wheels instead of compiling from source
|
|
424
|
+
// (avoids Rust/Cargo requirement for packages like tokenizers, crawl4ai)
|
|
425
|
+
execSync(`"${pyBin}" -m pip install -q --prefer-binary -r "${reqPath}"`, {
|
|
392
426
|
cwd: workspaceRoot, stdio: "inherit",
|
|
393
427
|
});
|
|
394
428
|
console.log(chalk.green(" ✓ Dependencies installed"));
|
|
@@ -415,29 +449,41 @@ program
|
|
|
415
449
|
else {
|
|
416
450
|
console.log(chalk.green(" ✓ Framework context files already up to date"));
|
|
417
451
|
}
|
|
418
|
-
// Step
|
|
452
|
+
// Step 3d — Copilot CLI enrichment pass.
|
|
453
|
+
// Only run when --with-copilot / --with-all is explicitly requested.
|
|
454
|
+
// Skipped silently on plain `igel-qe init` to avoid unexpected long waits.
|
|
455
|
+
const runEnrichment = options.withCopilot || options.withAll;
|
|
419
456
|
console.log(chalk.yellow("3d. Enriching context via GitHub Copilot CLI…"));
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
457
|
+
if (!runEnrichment) {
|
|
458
|
+
console.log(chalk.gray(" - Skipped (pass --with-copilot to enable Copilot context enrichment)"));
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
try {
|
|
462
|
+
const allContextFiles = listFrameworkContextFiles(workspaceRoot);
|
|
463
|
+
if (allContextFiles.length === 0) {
|
|
464
|
+
console.log(chalk.yellow(" ⚠ No context.md files found for Copilot enrichment"));
|
|
465
|
+
}
|
|
466
|
+
else if (!isCopilotCliAvailable()) {
|
|
467
|
+
console.log(chalk.yellow(" ⚠ Copilot CLI not installed; skipping Copilot enrichment"));
|
|
468
|
+
}
|
|
469
|
+
else if (!ensureCopilotLogin(workspaceRoot)) {
|
|
470
|
+
console.log(chalk.yellow(" ⚠ Copilot login was not completed; skipping Copilot enrichment"));
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
const cap = Math.min(allContextFiles.length, MAX_ENRICHMENT_FILES);
|
|
474
|
+
if (allContextFiles.length > MAX_ENRICHMENT_FILES) {
|
|
475
|
+
console.log(chalk.gray(` - ${allContextFiles.length} context files found; enriching first ${MAX_ENRICHMENT_FILES} (re-run to continue)`));
|
|
476
|
+
}
|
|
477
|
+
const run = enrichWithCopilotCli(workspaceRoot, allContextFiles);
|
|
478
|
+
console.log(chalk.green(` ✓ Copilot updated ${run.updated}/${cap} context.md file(s)`));
|
|
479
|
+
if (run.failed > 0) {
|
|
480
|
+
console.log(chalk.yellow(` ⚠ Copilot failed to update ${run.failed} context.md file(s)`));
|
|
481
|
+
}
|
|
436
482
|
}
|
|
437
483
|
}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
484
|
+
catch (e) {
|
|
485
|
+
console.log(chalk.yellow(` ⚠ Copilot context enrichment skipped: ${e instanceof Error ? e.message : String(e)}`));
|
|
486
|
+
}
|
|
441
487
|
}
|
|
442
488
|
// Step 3 — write MCP config (default: Copilot when no explicit platform flags)
|
|
443
489
|
console.log(chalk.yellow("4. Configuring MCP server…"));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "igel-qe-core",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "IGEL QE Developer Experience Layer (CLI & MCP)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"scripts": {
|
|
18
18
|
"build": "tsc",
|
|
19
|
+
"prepublishOnly": "tsc",
|
|
19
20
|
"start:mcp": "node ./dist/mcp/server.js"
|
|
20
21
|
},
|
|
21
22
|
"dependencies": {
|
package/src/cli/index.ts
CHANGED
|
@@ -232,37 +232,65 @@ function listFrameworkContextFiles(workspaceRoot: string): string[] {
|
|
|
232
232
|
}
|
|
233
233
|
|
|
234
234
|
function isCopilotCliAvailable(): boolean {
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
// Use a short timeout so this never blocks if copilot hangs on startup.
|
|
236
|
+
const check = spawnSync("copilot", ["--version"], {
|
|
237
|
+
encoding: "utf-8",
|
|
238
|
+
shell: process.platform === "win32",
|
|
239
|
+
timeout: 10_000,
|
|
240
|
+
});
|
|
241
|
+
return check.status === 0 && !check.error;
|
|
237
242
|
}
|
|
238
243
|
|
|
239
|
-
function isCopilotAuthenticated(
|
|
244
|
+
function isCopilotAuthenticated(): boolean {
|
|
245
|
+
// Use a lightweight auth-status check instead of spawning a full agent session.
|
|
246
|
+
// "copilot auth status" exits 0 when authenticated, non-zero otherwise.
|
|
247
|
+
// Fallback: if the sub-command doesn't exist, treat as unauthenticated.
|
|
240
248
|
const probe = spawnSync(
|
|
241
249
|
"copilot",
|
|
242
|
-
["
|
|
243
|
-
{
|
|
250
|
+
["auth", "status"],
|
|
251
|
+
{
|
|
252
|
+
encoding: "utf-8",
|
|
253
|
+
shell: process.platform === "win32",
|
|
254
|
+
timeout: 10_000, // 10 s max — simple status call should be instant
|
|
255
|
+
},
|
|
244
256
|
);
|
|
245
|
-
return probe.status === 0;
|
|
257
|
+
return probe.status === 0 && !probe.error;
|
|
246
258
|
}
|
|
247
259
|
|
|
248
260
|
function ensureCopilotLogin(workspaceRoot: string): boolean {
|
|
249
261
|
if (!isCopilotCliAvailable()) return false;
|
|
250
|
-
if (isCopilotAuthenticated(
|
|
262
|
+
if (isCopilotAuthenticated()) return true;
|
|
251
263
|
|
|
252
264
|
console.log(chalk.yellow(" ⚠ GitHub Copilot CLI is not authenticated."));
|
|
253
265
|
console.log(chalk.yellow(" → Please complete Copilot login to continue context enrichment."));
|
|
254
266
|
|
|
255
267
|
const login = spawnSync("copilot", ["login"], { stdio: "inherit", encoding: "utf-8", shell: process.platform === "win32" });
|
|
256
268
|
if (login.status !== 0) return false;
|
|
257
|
-
return isCopilotAuthenticated(
|
|
269
|
+
return isCopilotAuthenticated();
|
|
258
270
|
}
|
|
259
271
|
|
|
260
|
-
|
|
272
|
+
// Maximum number of context.md files to enrich per init run.
|
|
273
|
+
// Prevents extremely long waits on repos with many folders.
|
|
274
|
+
const MAX_ENRICHMENT_FILES = 15;
|
|
275
|
+
|
|
276
|
+
function enrichWithCopilotCli(
|
|
277
|
+
workspaceRoot: string,
|
|
278
|
+
contextFiles: string[],
|
|
279
|
+
): { updated: number; failed: number } {
|
|
261
280
|
let updated = 0;
|
|
262
281
|
let failed = 0;
|
|
263
282
|
|
|
264
|
-
|
|
265
|
-
|
|
283
|
+
// Limit the number of files processed in one init run.
|
|
284
|
+
const filesToProcess = contextFiles.slice(0, MAX_ENRICHMENT_FILES);
|
|
285
|
+
const total = filesToProcess.length;
|
|
286
|
+
|
|
287
|
+
for (let i = 0; i < filesToProcess.length; i++) {
|
|
288
|
+
const absPath = filesToProcess[i];
|
|
289
|
+
const relPath = absPath.replace(`${workspaceRoot}/`, "").replace(`${workspaceRoot}\\`, "");
|
|
290
|
+
|
|
291
|
+
// Show per-file progress so the user knows work is happening.
|
|
292
|
+
process.stdout.write(` [${i + 1}/${total}] Enriching ${relPath}…`);
|
|
293
|
+
|
|
266
294
|
const prompt = [
|
|
267
295
|
`Update the markdown file at path '${relPath}' in place.`,
|
|
268
296
|
"Goal: make it both human-understandable and LLM-ready for automation framework reasoning.",
|
|
@@ -284,12 +312,19 @@ function enrichWithCopilotCli(workspaceRoot: string, contextFiles: string[]): {
|
|
|
284
312
|
"--no-color",
|
|
285
313
|
"-s",
|
|
286
314
|
],
|
|
287
|
-
{
|
|
315
|
+
{
|
|
316
|
+
encoding: "utf-8",
|
|
317
|
+
shell: process.platform === "win32",
|
|
318
|
+
timeout: 120_000, // 2 min per file — prevents indefinite hangs
|
|
319
|
+
input: "", // close stdin immediately
|
|
320
|
+
},
|
|
288
321
|
);
|
|
289
322
|
|
|
290
|
-
if (result.status === 0) {
|
|
323
|
+
if (result.status === 0 && !result.error) {
|
|
324
|
+
process.stdout.write(" ✓\n");
|
|
291
325
|
updated += 1;
|
|
292
326
|
} else {
|
|
327
|
+
process.stdout.write(" ✗\n");
|
|
293
328
|
failed += 1;
|
|
294
329
|
}
|
|
295
330
|
}
|
|
@@ -418,7 +453,13 @@ program
|
|
|
418
453
|
console.log(chalk.yellow(" ⚠ requirements file not found; skipping Python dependency install"));
|
|
419
454
|
} else {
|
|
420
455
|
try {
|
|
421
|
-
|
|
456
|
+
const pyBin = getPythonBin(workspaceRoot);
|
|
457
|
+
// Wrap both the python path and requirements path in quotes so
|
|
458
|
+
// Windows paths that contain spaces (e.g. "IGEL Automation July 2026")
|
|
459
|
+
// are treated as a single argument by the shell.
|
|
460
|
+
// --prefer-binary: use pre-built wheels instead of compiling from source
|
|
461
|
+
// (avoids Rust/Cargo requirement for packages like tokenizers, crawl4ai)
|
|
462
|
+
execSync(`"${pyBin}" -m pip install -q --prefer-binary -r "${reqPath}"`, {
|
|
422
463
|
cwd: workspaceRoot, stdio: "inherit",
|
|
423
464
|
});
|
|
424
465
|
console.log(chalk.green(" ✓ Dependencies installed"));
|
|
@@ -446,25 +487,36 @@ program
|
|
|
446
487
|
}
|
|
447
488
|
|
|
448
489
|
|
|
449
|
-
// Step
|
|
490
|
+
// Step 3d — Copilot CLI enrichment pass.
|
|
491
|
+
// Only run when --with-copilot / --with-all is explicitly requested.
|
|
492
|
+
// Skipped silently on plain `igel-qe init` to avoid unexpected long waits.
|
|
493
|
+
const runEnrichment = options.withCopilot || options.withAll;
|
|
450
494
|
console.log(chalk.yellow("3d. Enriching context via GitHub Copilot CLI…"));
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
495
|
+
if (!runEnrichment) {
|
|
496
|
+
console.log(chalk.gray(" - Skipped (pass --with-copilot to enable Copilot context enrichment)"));
|
|
497
|
+
} else {
|
|
498
|
+
try {
|
|
499
|
+
const allContextFiles = listFrameworkContextFiles(workspaceRoot);
|
|
500
|
+
if (allContextFiles.length === 0) {
|
|
501
|
+
console.log(chalk.yellow(" ⚠ No context.md files found for Copilot enrichment"));
|
|
502
|
+
} else if (!isCopilotCliAvailable()) {
|
|
503
|
+
console.log(chalk.yellow(" ⚠ Copilot CLI not installed; skipping Copilot enrichment"));
|
|
504
|
+
} else if (!ensureCopilotLogin(workspaceRoot)) {
|
|
505
|
+
console.log(chalk.yellow(" ⚠ Copilot login was not completed; skipping Copilot enrichment"));
|
|
506
|
+
} else {
|
|
507
|
+
const cap = Math.min(allContextFiles.length, MAX_ENRICHMENT_FILES);
|
|
508
|
+
if (allContextFiles.length > MAX_ENRICHMENT_FILES) {
|
|
509
|
+
console.log(chalk.gray(` - ${allContextFiles.length} context files found; enriching first ${MAX_ENRICHMENT_FILES} (re-run to continue)`));
|
|
510
|
+
}
|
|
511
|
+
const run = enrichWithCopilotCli(workspaceRoot, allContextFiles);
|
|
512
|
+
console.log(chalk.green(` ✓ Copilot updated ${run.updated}/${cap} context.md file(s)`));
|
|
513
|
+
if (run.failed > 0) {
|
|
514
|
+
console.log(chalk.yellow(` ⚠ Copilot failed to update ${run.failed} context.md file(s)`));
|
|
515
|
+
}
|
|
464
516
|
}
|
|
517
|
+
} catch (e: unknown) {
|
|
518
|
+
console.log(chalk.yellow(` ⚠ Copilot context enrichment skipped: ${e instanceof Error ? e.message : String(e)}`));
|
|
465
519
|
}
|
|
466
|
-
} catch (e: unknown) {
|
|
467
|
-
console.log(chalk.yellow(` ⚠ Copilot context enrichment skipped: ${e instanceof Error ? e.message : String(e)}`));
|
|
468
520
|
}
|
|
469
521
|
|
|
470
522
|
// Step 3 — write MCP config (default: Copilot when no explicit platform flags)
|