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/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
- const isHelp = process.argv.includes("--help") || process.argv.includes("-h");
14
- const isForce = process.argv.includes("--force") || process.argv.includes("-f");
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 Overwrite existing AGENTS.md, .agent/jules.yml, and orchestration scripts.
26
- -h, --help Show this help message.
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.1.9",
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
- // 12. Makefile
167
- if (fs.existsSync(path.join(projectRoot, "Makefile"))) {
168
- return {
169
- testCmd: "make test",
170
- buildCmd: "make build",
171
- source: "Makefile",
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
- if (affectedPkgs.size === 0) return baseCmds;
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"))) {