jules-orchestrator-kit 0.29.0 ā 0.31.0
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/README.md +174 -89
- package/bin/agentctl.mjs +205 -6
- package/index.mjs +21 -1
- package/package.json +1 -1
- package/src/config.mjs +18 -2
- package/src/dag-engine.mjs +76 -0
- package/src/dashboard.mjs +2 -2
- package/src/engine.mjs +296 -37
- package/src/envelope.mjs +24 -2
- package/src/mcp.mjs +35 -1
- package/src/ops/checkpoint.mjs +167 -0
- package/src/ops/command-registry.mjs +274 -0
- package/src/ops/doctor-planner.mjs +214 -0
- package/src/ops/doctor-registry.mjs +442 -0
- package/src/ops/ide-scaffold.mjs +139 -0
- package/src/ops/receipts.mjs +171 -0
- package/src/ops/swarm-actions.mjs +156 -0
- package/src/ops/task-actions.mjs +214 -0
- package/src/ops/tdd-generator.mjs +115 -0
- package/src/ops/transaction.mjs +278 -0
- package/src/provider.mjs +164 -8
- package/src/security.mjs +15 -7
- package/src/stack-detector.mjs +15 -5
- package/src/state.mjs +44 -7
- package/src/task-optimizer.mjs +306 -0
- package/src/telemetry.mjs +35 -2
- package/src/tui.mjs +4 -1
- package/src/ux/capabilities.mjs +180 -0
- package/src/ux/diff-viewer.mjs +289 -0
- package/src/ux/key-decoder.mjs +281 -0
- package/src/ux/layout.mjs +195 -0
- package/src/ux/log-viewer.mjs +110 -0
- package/src/ux/palette.mjs +179 -0
- package/src/ux/queue-model.mjs +206 -0
- package/src/ux/renderer.mjs +188 -0
- package/src/ux/swarm-model.mjs +146 -0
- package/src/ux/terminal-session.mjs +215 -0
- package/src/ux/widgets.mjs +323 -0
- package/src/webhook.mjs +120 -0
- package/src/wizard-init.mjs +41 -11
- package/src/wizard-task.mjs +51 -15
package/bin/agentctl.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
5
|
-
import { join } from "node:path";
|
|
5
|
+
import { join, resolve } from "node:path";
|
|
6
6
|
import { loadConfig, resolveRoot, detectStack, bootstrapZeroTestRepo } from "../src/config.mjs";
|
|
7
7
|
import { gate, dispatch, run, isTaskFile } from "../src/engine.mjs";
|
|
8
8
|
import { acquireLock, releaseLock, lockStatus, checkDailyBudget, getQueueDir } from "../src/state.mjs";
|
|
@@ -14,7 +14,7 @@ const command = args[0];
|
|
|
14
14
|
|
|
15
15
|
function printHelp() {
|
|
16
16
|
console.log(`
|
|
17
|
-
š agentctl v0.
|
|
17
|
+
š agentctl v0.30.0 ā Universal Agent Orchestrator & Safety Gatekeeper
|
|
18
18
|
|
|
19
19
|
Usage: agentctl <command> [options]
|
|
20
20
|
|
|
@@ -32,6 +32,11 @@ Commands:
|
|
|
32
32
|
dashboard Start local HTTP telemetry and audit dashboard
|
|
33
33
|
init Scaffold .agent/ config and run onboarding wizard
|
|
34
34
|
task create Interactively author and scope a Jules task envelope
|
|
35
|
+
task optimize Linter & optimizer for Jules task prompts (--fix, --json)
|
|
36
|
+
test-gen Scaffold & run automated TDD Red-to-Green test cycle (--run)
|
|
37
|
+
mcp init Scaffold IDE integration config (cursor | vscode | claude | all)
|
|
38
|
+
rollback Restore git state & working tree to atomic pre-flight checkpoint
|
|
39
|
+
resume Resume warm session with human response (--response "<text>")
|
|
35
40
|
status Display queue and system status summary
|
|
36
41
|
scan Scan codebase for TODO/FIXME task candidates
|
|
37
42
|
version Output agentctl version
|
|
@@ -54,7 +59,7 @@ async function main() {
|
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
if (command === "version" || command === "--version" || command === "-v") {
|
|
57
|
-
console.log("agentctl v0.29.
|
|
62
|
+
console.log("agentctl v0.29.1");
|
|
58
63
|
process.exit(0);
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -265,7 +270,7 @@ async function main() {
|
|
|
265
270
|
}
|
|
266
271
|
|
|
267
272
|
case "doctor": {
|
|
268
|
-
console.log(`\nš agentctl System Diagnostics (v0.29.
|
|
273
|
+
console.log(`\nš agentctl System Diagnostics (v0.29.1)`);
|
|
269
274
|
console.log(`--------------------------------------------------`);
|
|
270
275
|
console.log(` Project Root : ${root}`);
|
|
271
276
|
console.log(` Config File : ${config._file || "None (Using defaults)"}`);
|
|
@@ -391,8 +396,66 @@ async function main() {
|
|
|
391
396
|
console.log(` Auto-PR : ${res.plan.flags.autoPr}`);
|
|
392
397
|
}
|
|
393
398
|
process.exit(0);
|
|
399
|
+
} else if (subCommand === "optimize") {
|
|
400
|
+
const { values, positionals } = parseArgs({
|
|
401
|
+
args: args.slice(2),
|
|
402
|
+
options: {
|
|
403
|
+
fix: { type: "boolean", short: "f" },
|
|
404
|
+
file: { type: "string" },
|
|
405
|
+
dir: { type: "string", short: "d" },
|
|
406
|
+
json: { type: "boolean", short: "j" },
|
|
407
|
+
"verify-cmd": { type: "string", short: "v" },
|
|
408
|
+
},
|
|
409
|
+
allowPositionals: true,
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const { scorePromptFalsifiability, optimizeTaskPrompt } = await import("../src/task-optimizer.mjs");
|
|
413
|
+
const targetDir = values.dir ? resolve(values.dir) : root;
|
|
414
|
+
let promptText = positionals.join(" ");
|
|
415
|
+
|
|
416
|
+
if (values.file) {
|
|
417
|
+
if (existsSync(values.file)) {
|
|
418
|
+
promptText = readFileSync(values.file, "utf-8");
|
|
419
|
+
} else {
|
|
420
|
+
console.error(`Error: File '${values.file}' does not exist.`);
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (values.fix) {
|
|
426
|
+
const opt = optimizeTaskPrompt(promptText, { rootDir: targetDir, verifyCmd: values["verify-cmd"] });
|
|
427
|
+
if (values.json) {
|
|
428
|
+
console.log(JSON.stringify(opt, null, 2));
|
|
429
|
+
} else {
|
|
430
|
+
console.log(opt.optimizedPrompt);
|
|
431
|
+
}
|
|
432
|
+
process.exit(0);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const analysis = scorePromptFalsifiability(promptText, { rootDir: targetDir, verifyCmd: values["verify-cmd"] });
|
|
436
|
+
if (values.json) {
|
|
437
|
+
console.log(JSON.stringify(analysis, null, 2));
|
|
438
|
+
} else {
|
|
439
|
+
console.log(`\nšÆ Task Prompt Falsifiability Analysis`);
|
|
440
|
+
console.log(`--------------------------------------------------`);
|
|
441
|
+
console.log(` Score / Grade : ${analysis.score} / 100 (${analysis.grade})`);
|
|
442
|
+
console.log(` Falsifiable : ${analysis.isFalsifiable ? "ā
YES" : "ā NO"}`);
|
|
443
|
+
if (analysis.oracle.command) {
|
|
444
|
+
console.log(` Oracle Command : "${analysis.oracle.command}" (${analysis.oracle.autoDetected ? "Auto-detected" : "User-supplied"})`);
|
|
445
|
+
}
|
|
446
|
+
if (analysis.issues.length > 0) {
|
|
447
|
+
console.log(`\n Issues Identified (${analysis.issues.length}):`);
|
|
448
|
+
analysis.issues.forEach((i) => console.log(` - [${i.type}] ${i.message} (-${i.penalty} pts)`));
|
|
449
|
+
}
|
|
450
|
+
if (analysis.suggestions.length > 0) {
|
|
451
|
+
console.log(`\n Suggestions for Improvement:`);
|
|
452
|
+
analysis.suggestions.forEach((s) => console.log(` - ${s}`));
|
|
453
|
+
}
|
|
454
|
+
console.log(`--------------------------------------------------\n`);
|
|
455
|
+
}
|
|
456
|
+
process.exit(analysis.isFalsifiable ? 0 : 1);
|
|
394
457
|
} else {
|
|
395
|
-
console.error(`Unknown task subcommand '${subCommand}'. Supported: agentctl task create`);
|
|
458
|
+
console.error(`Unknown task subcommand '${subCommand}'. Supported: agentctl task create, agentctl task optimize`);
|
|
396
459
|
process.exit(1);
|
|
397
460
|
}
|
|
398
461
|
break;
|
|
@@ -401,7 +464,7 @@ async function main() {
|
|
|
401
464
|
case "status": {
|
|
402
465
|
const queueDir = getQueueDir(root);
|
|
403
466
|
const files = existsSync(queueDir) ? readdirSync(queueDir).filter((f) => isTaskFile(f, queueDir)) : [];
|
|
404
|
-
console.log(`\nš agentctl Status Summary (v0.29.
|
|
467
|
+
console.log(`\nš agentctl Status Summary (v0.29.1)`);
|
|
405
468
|
console.log(`--------------------------------------------------`);
|
|
406
469
|
console.log(` Project Root : ${root}`);
|
|
407
470
|
console.log(` Pending Tasks : ${files.length}`);
|
|
@@ -427,7 +490,143 @@ async function main() {
|
|
|
427
490
|
break;
|
|
428
491
|
}
|
|
429
492
|
|
|
493
|
+
case "rollback": {
|
|
494
|
+
const { restoreCheckpoint } = await import("../src/ops/checkpoint.mjs");
|
|
495
|
+
const targetId = args[1] || "--latest";
|
|
496
|
+
try {
|
|
497
|
+
const res = restoreCheckpoint(targetId, { root });
|
|
498
|
+
console.log(`\nā
Git Checkpoint Restored Successfully!`);
|
|
499
|
+
console.log(` Session ID : ${res.id}`);
|
|
500
|
+
console.log(` HEAD SHA : ${res.headSha || "N/A"}`);
|
|
501
|
+
console.log(` RestoredAt : ${res.restoredAt}\n`);
|
|
502
|
+
process.exit(0);
|
|
503
|
+
} catch (err) {
|
|
504
|
+
console.error(`ā Rollback Failed: ${err.message}`);
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
break;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
case "resume": {
|
|
511
|
+
const { createProvider } = await import("../src/provider.mjs");
|
|
512
|
+
const sessionId = args[1];
|
|
513
|
+
const { values } = parseArgs({
|
|
514
|
+
args: args.slice(1),
|
|
515
|
+
options: {
|
|
516
|
+
response: { type: "string", short: "r" },
|
|
517
|
+
"dry-run": { type: "boolean", short: "d" },
|
|
518
|
+
json: { type: "boolean", short: "j" },
|
|
519
|
+
},
|
|
520
|
+
allowPositionals: true,
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
if (!sessionId || sessionId.startsWith("-")) {
|
|
524
|
+
console.error("Error: Session ID is required for agentctl resume <sessionId>.");
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const responseText = values.response || args.slice(2).join(" ");
|
|
529
|
+
if (!responseText) {
|
|
530
|
+
console.error("Error: --response text is required to resume warm session.");
|
|
531
|
+
process.exit(1);
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const provider = createProvider(config.provider || "jules", config);
|
|
535
|
+
try {
|
|
536
|
+
const res = await provider.resume(sessionId, responseText, { root, dryRun: values["dry-run"] });
|
|
537
|
+
if (values.json) {
|
|
538
|
+
console.log(JSON.stringify(res, null, 2));
|
|
539
|
+
} else {
|
|
540
|
+
console.log(`\nā
Warm Session Resumed Successfully!`);
|
|
541
|
+
console.log(` Session ID : ${res.id}`);
|
|
542
|
+
console.log(` Status : ${res.status}\n`);
|
|
543
|
+
}
|
|
544
|
+
process.exit(0);
|
|
545
|
+
} catch (err) {
|
|
546
|
+
console.error(`ā Resume Failed: ${err.message}`);
|
|
547
|
+
process.exit(1);
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
case "test-gen": {
|
|
553
|
+
const { scaffoldTddTest, runTddCycle } = await import("../src/ops/tdd-generator.mjs");
|
|
554
|
+
const { values } = parseArgs({
|
|
555
|
+
args: args.slice(1),
|
|
556
|
+
options: {
|
|
557
|
+
title: { type: "string", short: "t" },
|
|
558
|
+
spec: { type: "string", short: "s" },
|
|
559
|
+
run: { type: "boolean", short: "r" },
|
|
560
|
+
json: { type: "boolean", short: "j" },
|
|
561
|
+
},
|
|
562
|
+
allowPositionals: true,
|
|
563
|
+
});
|
|
564
|
+
|
|
565
|
+
const title = values.title || args[1] || "feature-spec";
|
|
566
|
+
const specText = values.spec || args.slice(2).join(" ") || "TDD requirement specification.";
|
|
567
|
+
|
|
568
|
+
try {
|
|
569
|
+
if (values.run) {
|
|
570
|
+
const res = await runTddCycle({ title, spec: specText }, { root });
|
|
571
|
+
if (values.json) {
|
|
572
|
+
console.log(JSON.stringify(res, null, 2));
|
|
573
|
+
} else {
|
|
574
|
+
console.log(`\nš“ TDD RED Check Verified!`);
|
|
575
|
+
console.log(` Test File : ${res.testFile}`);
|
|
576
|
+
console.log(` Scope Lock : Locked into scope.deny`);
|
|
577
|
+
console.log(` Status : Ready for green implementation dispatch\n`);
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
const scaffold = scaffoldTddTest({ title, spec: specText }, { root });
|
|
581
|
+
if (values.json) {
|
|
582
|
+
console.log(JSON.stringify(scaffold, null, 2));
|
|
583
|
+
} else {
|
|
584
|
+
console.log(`\nš§Ŗ TDD Test Scaffolded Successfully!`);
|
|
585
|
+
console.log(` File Path : ${scaffold.relativePath}`);
|
|
586
|
+
console.log(` Command : ${scaffold.testCmd}\n`);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
process.exit(0);
|
|
590
|
+
} catch (err) {
|
|
591
|
+
console.error(`ā TDD Generation Failed: ${err.message}`);
|
|
592
|
+
process.exit(1);
|
|
593
|
+
}
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
|
|
430
597
|
case "mcp": {
|
|
598
|
+
const subAction = args[1];
|
|
599
|
+
if (subAction === "init") {
|
|
600
|
+
const { scaffoldIdeConfig } = await import("../src/ops/ide-scaffold.mjs");
|
|
601
|
+
const { values } = parseArgs({
|
|
602
|
+
args: args.slice(2),
|
|
603
|
+
options: {
|
|
604
|
+
target: { type: "string", short: "t", default: "all" },
|
|
605
|
+
json: { type: "boolean", short: "j" },
|
|
606
|
+
},
|
|
607
|
+
allowPositionals: true,
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
const target = values.target || args[2] || "all";
|
|
611
|
+
try {
|
|
612
|
+
const res = scaffoldIdeConfig(target, { root });
|
|
613
|
+
if (values.json) {
|
|
614
|
+
console.log(JSON.stringify(res, null, 2));
|
|
615
|
+
} else {
|
|
616
|
+
console.log(`\nš IDE MCP Config Scaffolded Successfully!`);
|
|
617
|
+
console.log(` Target : ${res.target}`);
|
|
618
|
+
for (const item of res.results) {
|
|
619
|
+
console.log(` - ${item.target.toUpperCase()} : ${item.file}`);
|
|
620
|
+
}
|
|
621
|
+
console.log("");
|
|
622
|
+
}
|
|
623
|
+
process.exit(0);
|
|
624
|
+
} catch (err) {
|
|
625
|
+
console.error(`ā IDE MCP Scaffold Failed: ${err.message}`);
|
|
626
|
+
process.exit(1);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
431
630
|
const { startMcpServer } = await import("../src/mcp.mjs");
|
|
432
631
|
startMcpServer();
|
|
433
632
|
break;
|
package/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Google Jules Orchestrator Kit - Node.js SDK (v0.29.
|
|
2
|
+
* Google Jules Orchestrator Kit - Node.js SDK (v0.29.1)
|
|
3
3
|
*
|
|
4
4
|
* Exposes core orchestrator functions for programmatically driving agent tasks,
|
|
5
5
|
* security auditing, repo gating, and state operations.
|
|
@@ -85,4 +85,24 @@ export { planInit, loadPresets, runInitWizard, TIER_PROFILES, BUILTIN_PRESETS }
|
|
|
85
85
|
// Guided Task Authoring Subsystem
|
|
86
86
|
export { planTaskCreate, runTaskCreateWizard, GUARDRAIL_FOOTER } from "./src/wizard-task.mjs";
|
|
87
87
|
|
|
88
|
+
// Prompt Falsifiability & Task Optimizer Engine
|
|
89
|
+
export { scorePromptFalsifiability, optimizeTaskPrompt, levenshteinDistance, extractPathTokens } from "./src/task-optimizer.mjs";
|
|
90
|
+
|
|
91
|
+
// Atomic Git Checkpoints & Rollback
|
|
92
|
+
export { createCheckpoint, restoreCheckpoint, listCheckpoints, pruneCheckpoints, CheckpointError } from "./src/ops/checkpoint.mjs";
|
|
93
|
+
|
|
94
|
+
// Webhook & HITL Escalation Bridge
|
|
95
|
+
export { dispatchEscalation, verifySignature, parseWebhookPayload, routeWebhookEvent, createWebhookServer } from "./src/webhook.mjs";
|
|
96
|
+
|
|
97
|
+
// PR Review Evidence Bundler & Dev Server Probe
|
|
98
|
+
export { synthesizePrDescription, probeDevServer } from "./src/engine.mjs";
|
|
99
|
+
|
|
100
|
+
// Automated TDD Red-to-Green Harness
|
|
101
|
+
export { scaffoldTddTest, runTddCycle, TddError } from "./src/ops/tdd-generator.mjs";
|
|
102
|
+
|
|
103
|
+
// IDE Native MCP Scaffolder
|
|
104
|
+
export { scaffoldIdeConfig, IdeScaffoldError } from "./src/ops/ide-scaffold.mjs";
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
88
108
|
|
package/package.json
CHANGED
package/src/config.mjs
CHANGED
|
@@ -196,9 +196,19 @@ export function detectStack(projectRoot = process.cwd()) {
|
|
|
196
196
|
return detectPolyglotStack(projectRoot);
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
export function resolveVerify(root = process.cwd()) {
|
|
199
|
+
export function resolveVerify(root = process.cwd(), userVerify = {}) {
|
|
200
200
|
const s = detectStack(root);
|
|
201
|
-
return {
|
|
201
|
+
return {
|
|
202
|
+
setup: userVerify.setup ?? s.setupCmd ?? "",
|
|
203
|
+
test: userVerify.test ?? s.testCmd ?? "",
|
|
204
|
+
teardown: userVerify.teardown ?? s.teardownCmd ?? "",
|
|
205
|
+
build: userVerify.build ?? s.buildCmd ?? "",
|
|
206
|
+
server: userVerify.server ? {
|
|
207
|
+
command: userVerify.server.command || "",
|
|
208
|
+
url: userVerify.server.url || "http://localhost:3000",
|
|
209
|
+
timeoutMs: Number(userVerify.server.timeoutMs || 15000),
|
|
210
|
+
} : null,
|
|
211
|
+
};
|
|
202
212
|
}
|
|
203
213
|
|
|
204
214
|
export const TIER_PRESETS = {
|
|
@@ -247,8 +257,11 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
|
|
|
247
257
|
}
|
|
248
258
|
}
|
|
249
259
|
|
|
260
|
+
const setupCmd = parsed.setup_cmd || parsed.verify?.setup || "";
|
|
250
261
|
const testCmd = parsed.test_cmd || parsed.verify?.test || "";
|
|
262
|
+
const teardownCmd = parsed.teardown_cmd || parsed.verify?.teardown || "";
|
|
251
263
|
const buildCmd = parsed.build_cmd || parsed.verify?.build || "";
|
|
264
|
+
const verifyTimeoutMs = parsed.verify?.timeoutMs ?? parsed.verify?.timeout_ms ?? 60000;
|
|
252
265
|
const autoVerify = resolveVerify(root);
|
|
253
266
|
|
|
254
267
|
const activeTier = String(process.env.JULES_TIER || parsed.tier || "ultra").toLowerCase();
|
|
@@ -275,8 +288,11 @@ export function loadConfig(root = resolveRoot(), explicitPath = null) {
|
|
|
275
288
|
provider: parsed.provider || DEFAULTS.provider,
|
|
276
289
|
tier: activeTier,
|
|
277
290
|
verify: {
|
|
291
|
+
setup: setupCmd || autoVerify.setup || "",
|
|
278
292
|
test: testCmd || autoVerify.test,
|
|
293
|
+
teardown: teardownCmd || autoVerify.teardown || "",
|
|
279
294
|
build: buildCmd || autoVerify.build,
|
|
295
|
+
timeoutMs: Number.isFinite(Number(verifyTimeoutMs)) ? Number(verifyTimeoutMs) : 60000,
|
|
280
296
|
},
|
|
281
297
|
scope: normalizeScope(parsed),
|
|
282
298
|
limits: {
|
package/src/dag-engine.mjs
CHANGED
|
@@ -330,3 +330,79 @@ export class DagExecutor {
|
|
|
330
330
|
});
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Global Contract Trigger List:
|
|
336
|
+
* Files matching these patterns are considered global structural contracts.
|
|
337
|
+
* If ANY modified file matches these patterns, selective test execution MUST yield to full test suite execution (returns null).
|
|
338
|
+
*/
|
|
339
|
+
export const GLOBAL_CONTRACT_PATTERNS = [
|
|
340
|
+
/^package(-lock)?\.json$/,
|
|
341
|
+
/^pnpm-lock\.yaml$/,
|
|
342
|
+
/^yarn\.lock$/,
|
|
343
|
+
/^tsconfig.*\.json$/,
|
|
344
|
+
/^schema\./,
|
|
345
|
+
/\.d\.ts$/,
|
|
346
|
+
/^\.env/,
|
|
347
|
+
/^\.agent\//,
|
|
348
|
+
/^Cargo\.(toml|lock)$/,
|
|
349
|
+
/^go\.(mod|sum)$/,
|
|
350
|
+
/^composer\.(json|lock)$/,
|
|
351
|
+
];
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Checks if a relative file path matches global contract triggers.
|
|
355
|
+
*/
|
|
356
|
+
export function isGlobalContractFile(filePath = "") {
|
|
357
|
+
if (typeof filePath !== "string") return false;
|
|
358
|
+
const normalized = filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
359
|
+
const baseName = normalized.split("/").pop();
|
|
360
|
+
return GLOBAL_CONTRACT_PATTERNS.some((pattern) => pattern.test(normalized) || pattern.test(baseName));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Resolves affected test files based on modified files and static import analysis.
|
|
365
|
+
* Returns null if full test suite should be executed (due to global contract change or unknown dependency),
|
|
366
|
+
* or an array of affected test file paths.
|
|
367
|
+
*/
|
|
368
|
+
export function resolveAffectedTests(modifiedFiles = [], options = {}) {
|
|
369
|
+
if (!Array.isArray(modifiedFiles) || modifiedFiles.length === 0) {
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// 1. Check Global Contract Trigger List
|
|
374
|
+
for (const file of modifiedFiles) {
|
|
375
|
+
if (isGlobalContractFile(file)) {
|
|
376
|
+
return null; // Force full test suite execution
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const knownTestFiles = Array.isArray(options.knownTestFiles) ? options.knownTestFiles : [];
|
|
381
|
+
const testSuffixRegex = options.testSuffixRegex || /\.(test|spec)\.(m?[jt]sx?|py|rs|go)$/i;
|
|
382
|
+
|
|
383
|
+
const affected = new Set();
|
|
384
|
+
|
|
385
|
+
for (const file of modifiedFiles) {
|
|
386
|
+
if (typeof file !== "string") continue;
|
|
387
|
+
const normalized = file.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
388
|
+
|
|
389
|
+
// If the modified file itself is a test file, add it directly
|
|
390
|
+
if (testSuffixRegex.test(normalized)) {
|
|
391
|
+
affected.add(normalized);
|
|
392
|
+
continue;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// Match known test files that import or reference this file/module
|
|
396
|
+
const baseNameNoExt = normalized.split("/").pop().replace(/\.[^/.]+$/, "");
|
|
397
|
+
for (const testFile of knownTestFiles) {
|
|
398
|
+
if (typeof testFile !== "string") continue;
|
|
399
|
+
const normTest = testFile.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
400
|
+
if (normTest.includes(baseNameNoExt)) {
|
|
401
|
+
affected.add(normTest);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return affected.size > 0 ? Array.from(affected) : null;
|
|
407
|
+
}
|
|
408
|
+
|
package/src/dashboard.mjs
CHANGED
|
@@ -32,7 +32,7 @@ export function getDashboardHtml(root = process.cwd()) {
|
|
|
32
32
|
</style>
|
|
33
33
|
</head>
|
|
34
34
|
<body>
|
|
35
|
-
<h1>š jules-orchestrator-kit Dashboard (v0.29.
|
|
35
|
+
<h1>š jules-orchestrator-kit Dashboard (v0.29.1)</h1>
|
|
36
36
|
<div class="subtitle">Repository: <code>${root}</code></div>
|
|
37
37
|
|
|
38
38
|
<div class="grid">
|
|
@@ -87,7 +87,7 @@ export function createDashboardServer({ root = process.cwd(), port: _port = 4100
|
|
|
87
87
|
|
|
88
88
|
if (url.pathname === "/api/status") {
|
|
89
89
|
res.writeHead(200, { "Content-Type": "application/json; charset=utf-8" });
|
|
90
|
-
return res.end(JSON.stringify({ ok: true, version: "0.29.
|
|
90
|
+
return res.end(JSON.stringify({ ok: true, version: "0.29.1", root, ts: new Date().toISOString() }));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (url.pathname === "/api/telemetry") {
|