jules-orchestrator-kit 0.1.9 ā 0.2.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/.agent/history/2026-07-27-dispatch-test-swarm-task.md +9 -0
- package/.agent/jules-queue/README.md +8 -1
- package/.env.example +17 -0
- package/.github/workflows/jules-audit.yml +32 -0
- package/.github/workflows/jules-nightly.yml +21 -0
- package/.github/workflows/publish.yml +27 -0
- package/JULES_RULES_TEMPLATE.md +12 -8
- package/README.md +206 -78
- package/bin/init.js +93 -7
- package/package.json +3 -1
- package/scripts/command-resolver.mjs +88 -131
- package/scripts/jules-dispatch.mjs +165 -94
- package/scripts/jules-queue-runner.mjs +20 -2
- package/scripts/jules-self-audit.mjs +72 -5
- package/scripts/jules-swarm.mjs +85 -7
package/bin/init.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
7
|
+
import readline from "node:readline/promises";
|
|
6
8
|
import { resolveProjectCommands } from "../scripts/command-resolver.mjs";
|
|
7
9
|
|
|
8
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -10,8 +12,27 @@ const __dirname = path.dirname(__filename);
|
|
|
10
12
|
const kitRoot = path.resolve(__dirname, "..");
|
|
11
13
|
const targetDir = process.cwd();
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
let isHelp = false;
|
|
16
|
+
let isForce = false;
|
|
17
|
+
let isInteractive = false;
|
|
18
|
+
|
|
19
|
+
try {
|
|
20
|
+
const { values } = parseArgs({
|
|
21
|
+
options: {
|
|
22
|
+
help: { type: "boolean", short: "h", default: false },
|
|
23
|
+
force: { type: "boolean", short: "f", default: false },
|
|
24
|
+
interactive: { type: "boolean", short: "i", default: false },
|
|
25
|
+
},
|
|
26
|
+
allowPositionals: true,
|
|
27
|
+
});
|
|
28
|
+
isHelp = values.help;
|
|
29
|
+
isForce = values.force;
|
|
30
|
+
isInteractive = values.interactive;
|
|
31
|
+
} catch (err) {
|
|
32
|
+
isHelp = process.argv.includes("--help") || process.argv.includes("-h");
|
|
33
|
+
isForce = process.argv.includes("--force") || process.argv.includes("-f");
|
|
34
|
+
isInteractive = process.argv.includes("--interactive") || process.argv.includes("-i");
|
|
35
|
+
}
|
|
15
36
|
|
|
16
37
|
if (isHelp) {
|
|
17
38
|
console.log(`
|
|
@@ -22,8 +43,9 @@ Usage:
|
|
|
22
43
|
npx jules-init [options]
|
|
23
44
|
|
|
24
45
|
Options:
|
|
25
|
-
-f, --force
|
|
26
|
-
-
|
|
46
|
+
-f, --force Overwrite existing AGENTS.md, .agent/jules.yml, and orchestration scripts.
|
|
47
|
+
-i, --interactive Launch interactive wizard to prompt for repository and branch configuration.
|
|
48
|
+
-h, --help Show this help message.
|
|
27
49
|
`);
|
|
28
50
|
process.exit(0);
|
|
29
51
|
}
|
|
@@ -32,6 +54,52 @@ console.log("\nš Initializing Google Jules Orchestration Kit...\n");
|
|
|
32
54
|
console.log(`š Target Directory: ${targetDir}`);
|
|
33
55
|
if (isForce) console.log("ā ļø Force mode enabled (existing files will be overwritten).");
|
|
34
56
|
|
|
57
|
+
let answerRepoVal = "";
|
|
58
|
+
let answerBranchVal = "";
|
|
59
|
+
|
|
60
|
+
// Dual TTY Interactive Wizard
|
|
61
|
+
if (process.stdin.isTTY && (isInteractive || (!fs.existsSync(path.join(targetDir, ".agent/jules.yml")) && !process.env.CI))) {
|
|
62
|
+
try {
|
|
63
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
64
|
+
const answerRepo = await rl.question("š¦ Enter target GitHub repository (e.g. owner/repo) [optional]: ");
|
|
65
|
+
if (answerRepo.trim()) {
|
|
66
|
+
answerRepoVal = answerRepo.trim();
|
|
67
|
+
process.env.JULES_REPO = answerRepoVal;
|
|
68
|
+
}
|
|
69
|
+
const answerBranch = await rl.question("šæ Enter base branch [default: main]: ");
|
|
70
|
+
if (answerBranch.trim()) {
|
|
71
|
+
answerBranchVal = answerBranch.trim();
|
|
72
|
+
process.env.BASE_BRANCH = answerBranchVal;
|
|
73
|
+
}
|
|
74
|
+
rl.close();
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.warn("ā ļø Interactive setup prompt failed:", err.message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// 0. Persist wizard values to .env if provided
|
|
81
|
+
const envPath = path.join(targetDir, ".env");
|
|
82
|
+
let envAdditions = [];
|
|
83
|
+
if (answerRepoVal) envAdditions.push(`JULES_REPO="${answerRepoVal}"`);
|
|
84
|
+
if (answerBranchVal) envAdditions.push(`BASE_BRANCH="${answerBranchVal}"`);
|
|
85
|
+
|
|
86
|
+
if (envAdditions.length > 0) {
|
|
87
|
+
if (fs.existsSync(envPath)) {
|
|
88
|
+
const existingEnv = fs.readFileSync(envPath, "utf-8");
|
|
89
|
+
const toAppend = envAdditions.filter((line) => {
|
|
90
|
+
const key = line.split("=")[0];
|
|
91
|
+
return !existingEnv.includes(`${key}=`);
|
|
92
|
+
});
|
|
93
|
+
if (toAppend.length > 0) {
|
|
94
|
+
fs.appendFileSync(envPath, `\n${toAppend.join("\n")}\n`, "utf-8");
|
|
95
|
+
console.log("ā
Appended interactive configuration to .env");
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
fs.writeFileSync(envPath, `${envAdditions.join("\n")}\n`, "utf-8");
|
|
99
|
+
console.log("ā
Created: .env with repository configuration");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
35
103
|
// 1. Detect Stack & Manifests
|
|
36
104
|
const detected = resolveProjectCommands(targetDir);
|
|
37
105
|
console.log(`š Detected Project Type: ${detected.source}`);
|
|
@@ -102,6 +170,21 @@ if ((!fs.existsSync(reviewTarget) || isForce) && fs.existsSync(reviewSource)) {
|
|
|
102
170
|
console.log("ā
Created: .agent/workflows/jules-review.md");
|
|
103
171
|
}
|
|
104
172
|
|
|
173
|
+
// Scaffold .github/workflows/jules-audit.yml
|
|
174
|
+
const githubWorkflowsDir = path.join(targetDir, ".github/workflows");
|
|
175
|
+
const auditWfSource = path.join(kitRoot, ".github/workflows/jules-audit.yml");
|
|
176
|
+
const auditWfTarget = path.join(githubWorkflowsDir, "jules-audit.yml");
|
|
177
|
+
|
|
178
|
+
if (fs.existsSync(auditWfSource)) {
|
|
179
|
+
if (!fs.existsSync(githubWorkflowsDir)) {
|
|
180
|
+
fs.mkdirSync(githubWorkflowsDir, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
if (!fs.existsSync(auditWfTarget) || isForce) {
|
|
183
|
+
fs.copyFileSync(auditWfSource, auditWfTarget);
|
|
184
|
+
console.log("ā
Scaffolded CI Audit Workflow: .github/workflows/jules-audit.yml");
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
105
188
|
// 4. Copy scripts/ directory with PER-FILE existence guard
|
|
106
189
|
const targetScriptsDir = path.join(targetDir, "scripts");
|
|
107
190
|
if (!fs.existsSync(targetScriptsDir)) {
|
|
@@ -121,7 +204,9 @@ if (fs.existsSync(sourceScriptsDir)) {
|
|
|
121
204
|
fs.copyFileSync(srcFile, destFile);
|
|
122
205
|
try {
|
|
123
206
|
fs.chmodSync(destFile, 0o755);
|
|
124
|
-
} catch (
|
|
207
|
+
} catch (err) {
|
|
208
|
+
console.warn(`ā ļø Could not set executable permissions on ${file}:`, err.message);
|
|
209
|
+
}
|
|
125
210
|
copiedCount++;
|
|
126
211
|
} else {
|
|
127
212
|
skippedCount++;
|
|
@@ -156,7 +241,9 @@ if (fs.existsSync(targetPkgPath) && targetDir !== kitRoot) {
|
|
|
156
241
|
fs.writeFileSync(targetPkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
157
242
|
console.log("ā
Injected jules:* helper scripts into package.json");
|
|
158
243
|
}
|
|
159
|
-
} catch (
|
|
244
|
+
} catch (err) {
|
|
245
|
+
console.warn("ā ļø Failed to inject helper scripts into target package.json:", err.message);
|
|
246
|
+
}
|
|
160
247
|
}
|
|
161
248
|
|
|
162
249
|
console.log("\nš Google Jules Orchestration Kit successfully initialized!");
|
|
@@ -164,4 +251,3 @@ console.log("\nNext Steps:");
|
|
|
164
251
|
console.log(" 1. Set environment variables: JULES_REPO=\"owner/repo\"");
|
|
165
252
|
console.log(" 2. Dispatch your first task: node scripts/jules-dispatch.mjs \"Task Title\" \"Task prompt\"");
|
|
166
253
|
console.log(" 3. Run pre-merge PR audit: node scripts/jules-self-audit.mjs\n");
|
|
167
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jules-orchestrator-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/FullThrottle83/jules-orchestrator-kit.git"
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
"bin/",
|
|
19
19
|
"scripts/",
|
|
20
20
|
".agent/",
|
|
21
|
+
".github/",
|
|
22
|
+
".env.example",
|
|
21
23
|
"JULES_RULES_TEMPLATE.md",
|
|
22
24
|
"README.md",
|
|
23
25
|
"LICENSE"
|
|
@@ -39,137 +39,90 @@ export function resolveProjectCommands(projectRoot = process.cwd()) {
|
|
|
39
39
|
buildCmd: cfg.buildCmd || "",
|
|
40
40
|
source: "jules.config.json",
|
|
41
41
|
};
|
|
42
|
-
} catch (
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// 2. Bun (bunfig.toml or bun.lockb)
|
|
46
|
-
if (
|
|
47
|
-
fs.existsSync(path.join(projectRoot, "bunfig.toml")) ||
|
|
48
|
-
fs.existsSync(path.join(projectRoot, "bun.lockb"))
|
|
49
|
-
) {
|
|
50
|
-
return {
|
|
51
|
-
testCmd: "bun test",
|
|
52
|
-
buildCmd: "bun run build",
|
|
53
|
-
source: "Bun Manifest",
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 3. Deno (deno.json / deno.jsonc)
|
|
58
|
-
if (
|
|
59
|
-
fs.existsSync(path.join(projectRoot, "deno.json")) ||
|
|
60
|
-
fs.existsSync(path.join(projectRoot, "deno.jsonc"))
|
|
61
|
-
) {
|
|
62
|
-
return {
|
|
63
|
-
testCmd: "deno test",
|
|
64
|
-
buildCmd: "deno task build",
|
|
65
|
-
source: "Deno Manifest",
|
|
66
|
-
};
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 4. JavaScript / TypeScript (package.json)
|
|
70
|
-
const pkgPath = path.join(projectRoot, "package.json");
|
|
71
|
-
if (fs.existsSync(pkgPath)) {
|
|
72
|
-
try {
|
|
73
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
74
|
-
const scripts = pkg.scripts || {};
|
|
75
|
-
const verifyScript = scripts["check:all"]
|
|
76
|
-
? "npm run check:all"
|
|
77
|
-
: scripts.test
|
|
78
|
-
? "npm test"
|
|
79
|
-
: "";
|
|
80
|
-
const buildScript = scripts.build ? "npm run build" : "";
|
|
81
|
-
return {
|
|
82
|
-
testCmd: verifyScript,
|
|
83
|
-
buildCmd: buildScript,
|
|
84
|
-
source: "package.json",
|
|
85
|
-
};
|
|
86
|
-
} catch (_) {}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// 5. Rust (Cargo.toml)
|
|
90
|
-
if (fs.existsSync(path.join(projectRoot, "Cargo.toml"))) {
|
|
91
|
-
return {
|
|
92
|
-
testCmd: "cargo test --workspace",
|
|
93
|
-
buildCmd: "cargo build",
|
|
94
|
-
source: "Cargo.toml",
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// 6. Go (go.mod)
|
|
99
|
-
if (fs.existsSync(path.join(projectRoot, "go.mod"))) {
|
|
100
|
-
return {
|
|
101
|
-
testCmd: "go test ./...",
|
|
102
|
-
buildCmd: "go build ./...",
|
|
103
|
-
source: "go.mod",
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// 7. Python (pyproject.toml / requirements.txt / setup.py)
|
|
108
|
-
if (
|
|
109
|
-
fs.existsSync(path.join(projectRoot, "pyproject.toml")) ||
|
|
110
|
-
fs.existsSync(path.join(projectRoot, "requirements.txt")) ||
|
|
111
|
-
fs.existsSync(path.join(projectRoot, "setup.py"))
|
|
112
|
-
) {
|
|
113
|
-
return {
|
|
114
|
-
testCmd: "pytest",
|
|
115
|
-
buildCmd: "",
|
|
116
|
-
source: "Python Manifest",
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// 8. Elixir (mix.exs)
|
|
121
|
-
if (fs.existsSync(path.join(projectRoot, "mix.exs"))) {
|
|
122
|
-
return {
|
|
123
|
-
testCmd: "mix test",
|
|
124
|
-
buildCmd: "mix compile",
|
|
125
|
-
source: "mix.exs",
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// 9. Ruby (Gemfile)
|
|
130
|
-
if (fs.existsSync(path.join(projectRoot, "Gemfile"))) {
|
|
131
|
-
return {
|
|
132
|
-
testCmd: "bundle exec rake test",
|
|
133
|
-
buildCmd: "",
|
|
134
|
-
source: "Gemfile",
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// 10. Swift (Package.swift)
|
|
139
|
-
if (fs.existsSync(path.join(projectRoot, "Package.swift"))) {
|
|
140
|
-
return {
|
|
141
|
-
testCmd: "swift test",
|
|
142
|
-
buildCmd: "swift build",
|
|
143
|
-
source: "Package.swift",
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// 11. Java (Maven pom.xml / Gradle build.gradle)
|
|
148
|
-
if (fs.existsSync(path.join(projectRoot, "pom.xml"))) {
|
|
149
|
-
return {
|
|
150
|
-
testCmd: "mvn test",
|
|
151
|
-
buildCmd: "mvn compile",
|
|
152
|
-
source: "pom.xml",
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
if (
|
|
156
|
-
fs.existsSync(path.join(projectRoot, "build.gradle")) ||
|
|
157
|
-
fs.existsSync(path.join(projectRoot, "build.gradle.kts"))
|
|
158
|
-
) {
|
|
159
|
-
return {
|
|
160
|
-
testCmd: "./gradlew test",
|
|
161
|
-
buildCmd: "./gradlew assemble",
|
|
162
|
-
source: "build.gradle",
|
|
163
|
-
};
|
|
42
|
+
} catch (err) {
|
|
43
|
+
console.warn("ā ļø Failed to parse jules.config.json:", err.message);
|
|
44
|
+
}
|
|
164
45
|
}
|
|
165
46
|
|
|
166
|
-
//
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
buildCmd: "
|
|
171
|
-
|
|
172
|
-
|
|
47
|
+
// 2. Dynamic Detector Strategy List
|
|
48
|
+
const detectors = [
|
|
49
|
+
{
|
|
50
|
+
files: ["bunfig.toml", "bun.lockb"],
|
|
51
|
+
resolve: () => ({ testCmd: "bun test", buildCmd: "bun run build", source: "Bun Manifest" }),
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
files: ["deno.json", "deno.jsonc"],
|
|
55
|
+
resolve: () => ({ testCmd: "deno test", buildCmd: "deno task build", source: "Deno Manifest" }),
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
files: ["package.json"],
|
|
59
|
+
resolve: (root) => {
|
|
60
|
+
try {
|
|
61
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf-8"));
|
|
62
|
+
const scripts = pkg.scripts || {};
|
|
63
|
+
let verifyScript = scripts["check:all"] ? "npm run check:all" : "";
|
|
64
|
+
if (!verifyScript) {
|
|
65
|
+
const parts = [];
|
|
66
|
+
if (scripts.lint) parts.push("npm run lint");
|
|
67
|
+
else if (scripts.check) parts.push("npm run check");
|
|
68
|
+
if (scripts.test) parts.push("npm test");
|
|
69
|
+
verifyScript = parts.join(" && ");
|
|
70
|
+
}
|
|
71
|
+
const buildScript = scripts.build ? "npm run build" : "";
|
|
72
|
+
return {
|
|
73
|
+
testCmd: verifyScript || "npm test",
|
|
74
|
+
buildCmd: buildScript,
|
|
75
|
+
source: "package.json",
|
|
76
|
+
};
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.warn("ā ļø Failed to parse package.json:", err.message);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
files: ["Cargo.toml"],
|
|
85
|
+
resolve: () => ({ testCmd: "cargo test --workspace", buildCmd: "cargo build", source: "Cargo.toml" }),
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
files: ["go.mod"],
|
|
89
|
+
resolve: () => ({ testCmd: "go test ./...", buildCmd: "go build ./...", source: "go.mod" }),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
files: ["pyproject.toml", "requirements.txt", "setup.py"],
|
|
93
|
+
resolve: () => ({ testCmd: "pytest", buildCmd: "", source: "Python Manifest" }),
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
files: ["mix.exs"],
|
|
97
|
+
resolve: () => ({ testCmd: "mix test", buildCmd: "mix compile", source: "mix.exs" }),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
files: ["Gemfile"],
|
|
101
|
+
resolve: () => ({ testCmd: "bundle exec rake test", buildCmd: "", source: "Gemfile" }),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
files: ["Package.swift"],
|
|
105
|
+
resolve: () => ({ testCmd: "swift test", buildCmd: "swift build", source: "Package.swift" }),
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
files: ["pom.xml"],
|
|
109
|
+
resolve: () => ({ testCmd: "mvn test", buildCmd: "mvn compile", source: "pom.xml" }),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
files: ["build.gradle", "build.gradle.kts"],
|
|
113
|
+
resolve: () => ({ testCmd: "./gradlew test", buildCmd: "./gradlew assemble", source: "build.gradle" }),
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
files: ["Makefile"],
|
|
117
|
+
resolve: () => ({ testCmd: "make test", buildCmd: "make build", source: "Makefile" }),
|
|
118
|
+
},
|
|
119
|
+
];
|
|
120
|
+
|
|
121
|
+
for (const detector of detectors) {
|
|
122
|
+
if (detector.files.some((f) => fs.existsSync(path.join(projectRoot, f)))) {
|
|
123
|
+
const res = detector.resolve(projectRoot);
|
|
124
|
+
if (res) return res;
|
|
125
|
+
}
|
|
173
126
|
}
|
|
174
127
|
|
|
175
128
|
return {
|
|
@@ -185,6 +138,9 @@ export function resolveProjectCommands(projectRoot = process.cwd()) {
|
|
|
185
138
|
*/
|
|
186
139
|
export function resolveWorkspaceExecutionBoundary(modifiedFiles = [], projectRoot = process.cwd()) {
|
|
187
140
|
const baseCmds = resolveProjectCommands(projectRoot);
|
|
141
|
+
if (baseCmds.source === ".agent/jules.yml" || baseCmds.source === "jules.config.json") {
|
|
142
|
+
return baseCmds;
|
|
143
|
+
}
|
|
188
144
|
if (!modifiedFiles || modifiedFiles.length === 0) return baseCmds;
|
|
189
145
|
|
|
190
146
|
// Find affected package names by walking up from modified files to nearest manifest
|
|
@@ -211,8 +167,9 @@ export function resolveWorkspaceExecutionBoundary(modifiedFiles = [], projectRoo
|
|
|
211
167
|
}
|
|
212
168
|
}
|
|
213
169
|
|
|
214
|
-
|
|
215
|
-
const targets = Array.from(affectedPkgs);
|
|
170
|
+
const SAFE_PKG_NAME = /^[@a-zA-Z0-9._\/-]+$/;
|
|
171
|
+
const targets = Array.from(affectedPkgs).filter((t) => SAFE_PKG_NAME.test(t));
|
|
172
|
+
if (targets.length === 0) return baseCmds;
|
|
216
173
|
|
|
217
174
|
// 1. Turborepo (turbo.json)
|
|
218
175
|
if (fs.existsSync(path.join(projectRoot, "turbo.json"))) {
|