juno-code 1.0.38 → 1.0.41

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 CHANGED
@@ -270,6 +270,99 @@ Then run with the hook:
270
270
  ./.juno_task/scripts/run_until_completion.sh --pre-run-hook SLACK_SYNC -s claude -i 5 -v
271
271
  ```
272
272
 
273
+ ## GitHub Integration
274
+
275
+ juno-code includes built-in GitHub integration for issue tracking and automated responses. The system monitors GitHub repositories, creates kanban tasks from issues, and posts agent responses as threaded comments with automatic issue closure.
276
+
277
+ ### How It Works
278
+
279
+ 1. **Fetch**: `github.py fetch` monitors a GitHub repository and creates kanban tasks from new issues
280
+ 2. **Process**: The AI agent processes tasks and records responses in the kanban
281
+ 3. **Respond**: `github.py respond` posts agent responses as comments on GitHub issues and closes them
282
+
283
+ This enables a workflow where team members can submit tasks via GitHub issues and receive AI-generated responses with automatic issue closure.
284
+
285
+ ### Setup
286
+
287
+ 1. **Create a GitHub Personal Access Token**:
288
+ - Go to https://github.com/settings/tokens and create a new token (classic)
289
+ - Grant these permissions:
290
+ - `repo` (full control of private repositories)
291
+ - `public_repo` (access to public repositories)
292
+ - Copy the token (starts with `ghp_`)
293
+
294
+ 2. **Configure Environment**:
295
+ ```bash
296
+ # In project root .env file
297
+ GITHUB_TOKEN=ghp_your_token_here
298
+ GITHUB_REPO=owner/repo # Optional default repository
299
+ GITHUB_LABELS=bug,priority # Optional label filter
300
+ ```
301
+
302
+ 3. **Usage**:
303
+ ```bash
304
+ # Fetch issues from GitHub and create tasks
305
+ ./.juno_task/scripts/github.py fetch --repo owner/repo
306
+
307
+ # Filter by labels
308
+ ./.juno_task/scripts/github.py fetch --repo owner/repo --labels bug,priority
309
+
310
+ # Post completed task responses back to GitHub
311
+ ./.juno_task/scripts/github.py respond --tag github-issue
312
+
313
+ # Bidirectional sync (fetch + respond)
314
+ ./.juno_task/scripts/github.py sync --repo owner/repo
315
+
316
+ # Continuous sync mode with interval
317
+ ./.juno_task/scripts/github.py sync --repo owner/repo --continuous --interval 600
318
+
319
+ # One-time sync
320
+ ./.juno_task/scripts/github.py sync --repo owner/repo --once
321
+
322
+ # Dry run to preview what would be posted
323
+ ./.juno_task/scripts/github.py respond --dry-run --verbose
324
+ ```
325
+
326
+ ### Key Features
327
+
328
+ - **Tag-based identification**: Uses `github_issue_owner_repo_123` format for O(1) lookups
329
+ - **State tracking**: Maintains state in `.juno_task/github/state.ndjson` and `responses.ndjson`
330
+ - **Automatic closure**: Issues are automatically closed after posting the agent response
331
+ - **Commit linking**: Includes commit hash in comment if available
332
+ - **Response format**: Posts `agent_response` field from completed kanban tasks
333
+
334
+ ### Automated GitHub Workflow with Hooks
335
+
336
+ Use the `--pre-run` flag to sync with GitHub before each juno-code run:
337
+
338
+ ```bash
339
+ # Fetch GitHub issues before starting work
340
+ ./.juno_task/scripts/run_until_completion.sh \
341
+ --pre-run "./.juno_task/scripts/github.py fetch --repo owner/repo" \
342
+ -s claude -i 5 -v
343
+ ```
344
+
345
+ Or configure hooks in `.juno_task/config.json`:
346
+
347
+ ```json
348
+ {
349
+ "hooks": {
350
+ "GITHUB_SYNC": {
351
+ "commands": [
352
+ "./.juno_task/scripts/github.py fetch --repo owner/repo --labels bug",
353
+ "./.juno_task/scripts/github.py respond --tag github-issue"
354
+ ]
355
+ }
356
+ }
357
+ }
358
+ ```
359
+
360
+ Then run with the hook:
361
+
362
+ ```bash
363
+ ./.juno_task/scripts/run_until_completion.sh --pre-run-hook GITHUB_SYNC -s claude -i 5 -v
364
+ ```
365
+
273
366
  ## run_until_completion.sh
274
367
 
275
368
  The `run_until_completion.sh` script continuously runs juno-code until all kanban tasks are completed. It uses a do-while loop pattern: juno-code runs at least once, then continues while tasks remain in backlog, todo, or in_progress status.
package/dist/bin/cli.js CHANGED
@@ -1337,7 +1337,7 @@ var init_config = __esm({
1337
1337
  DEFAULT_CONFIG = {
1338
1338
  // Core settings
1339
1339
  defaultSubagent: "claude",
1340
- defaultBackend: "mcp",
1340
+ defaultBackend: "shell",
1341
1341
  defaultMaxIterations: 50,
1342
1342
  // Logging settings
1343
1343
  logLevel: "info",
@@ -13426,8 +13426,11 @@ var init_script_installer = __esm({
13426
13426
  // Wrapper script for Slack fetch
13427
13427
  "slack_respond.py",
13428
13428
  // Core logic for sending responses to Slack
13429
- "slack_respond.sh"
13429
+ "slack_respond.sh",
13430
13430
  // Wrapper script for Slack respond
13431
+ // GitHub integration script (single-file architecture)
13432
+ "github.py"
13433
+ // Unified GitHub integration (fetch, respond, sync)
13431
13434
  ];
13432
13435
  /**
13433
13436
  * Get the templates scripts directory from the package
@@ -14044,15 +14047,17 @@ async function validateStartupConfigs(baseDir = process.cwd(), verbose = false)
14044
14047
  console.error(chalk15__default.default.blue("\u{1F50D} Validating JSON configuration files...\n"));
14045
14048
  }
14046
14049
  try {
14047
- let backendType = "mcp";
14050
+ let backendType = "shell";
14051
+ let backendSetViaCliArg = false;
14048
14052
  const backendArgIndex = process.argv.findIndex((arg) => arg === "-b" || arg === "--backend");
14049
14053
  if (backendArgIndex !== -1 && process.argv[backendArgIndex + 1]) {
14050
14054
  const cliBackend = process.argv[backendArgIndex + 1].toLowerCase().trim();
14051
14055
  if (cliBackend === "shell" || cliBackend === "mcp") {
14052
14056
  backendType = cliBackend;
14057
+ backendSetViaCliArg = true;
14053
14058
  }
14054
14059
  }
14055
- if (backendType === "mcp") {
14060
+ if (!backendSetViaCliArg) {
14056
14061
  const envBackend = process.env.JUNO_CODE_AGENT || process.env.JUNO_CODE_BACKEND || process.env.JUNO_TASK_BACKEND;
14057
14062
  if (envBackend) {
14058
14063
  const normalized = envBackend.toLowerCase().trim();
@@ -14551,22 +14556,19 @@ and for each part create a seperate .md file under @.juno_task/spec/*
14551
14556
 
14552
14557
  ## ULTIMATE Goal
14553
14558
  We want to achieve the main Task with respect to the Constraints section
14559
+
14560
+ Part 1)
14554
14561
  Consider missing steps and plan. If the step is missing then author the specification at @.juno_task/spec/FILENAME.md (do NOT assume that it does not exist, search before creating). The naming of the module should be GenZ named and not conflict with another module name. If you create a new step then document the plan to implement in @.juno_task/plan.md
14555
14562
 
14556
14563
 
14564
+ Part 2) after completing the plan, and spec, create task for implementing each part on kanban './.juno_task/scripts/kanban.sh' You need to create a task for each step of implementation and testing. You need to go through the project, the spec and plan at the end to make sure you have covered all tasks on the kanban. We will later on implement tasks from kanban one by one.
14565
+ After completing the proccess an implementer agent would start the job and go through kanban tasks one by one.
14566
+
14567
+
14557
14568
  ### Constraints
14558
14569
  **Preferred Subagent**: {{SUBAGENT}}
14559
14570
  **Repository URL**: {{GIT_URL}}
14560
-
14561
-
14562
- ## Environment Setup
14563
- [Empty]
14564
-
14565
- ### 2. Package Installation
14566
- [Empty]
14567
-
14568
- ### 3. Test Installation
14569
- [Empty]`,
14571
+ `,
14570
14572
  variables: [
14571
14573
  {
14572
14574
  name: "main_task",
@@ -14911,6 +14913,23 @@ Items that have been resolved will be moved here.`,
14911
14913
  **Git Repository:** {{GIT_URL}}
14912
14914
  **Configuration Date:** {{CURRENT_DATE}}
14913
14915
 
14916
+ ## Kanban Task Management
14917
+
14918
+ \`\`\`bash
14919
+ # List tasks
14920
+ ./.juno_task/scripts/kanban.sh list --limit 5 --sort asc
14921
+ ./.juno_task/scripts/kanban.sh list --status [backlog|todo|in_progress|done] --sort asc
14922
+
14923
+ # Task operations
14924
+ ./.juno_task/scripts/kanban.sh get {TASK_ID}
14925
+ ./.juno_task/scripts/kanban.sh mark [in_progress|done|todo] --id {TASK_ID} --response "message"
14926
+ ./.juno_task/scripts/kanban.sh update {TASK_ID} --commit {COMMIT_HASH}
14927
+ \`\`\`
14928
+
14929
+ When a task on kanban, has related_tasks key, you need to get the task to understand the complete picture of tasks related to the current current task, you can get all the context through
14930
+ \`./.juno_task/scripts/kanban.sh get {TASK_ID}\`
14931
+
14932
+
14914
14933
  ## Agent-Specific Instructions
14915
14934
 
14916
14935
  ### {{SUBAGENT}} Configuration
@@ -15025,6 +15044,24 @@ python -m pytest tests/ --cov=src --cov-report=term-missing
15025
15044
  - **Code Quality:** Focus on production-ready, well-documented code
15026
15045
  - **Testing:** Comprehensive unit and integration tests required
15027
15046
 
15047
+ ## Kanban Task Management
15048
+
15049
+ \`\`\`bash
15050
+ # List tasks
15051
+ ./.juno_task/scripts/kanban.sh list --limit 5 --sort asc
15052
+ ./.juno_task/scripts/kanban.sh list --status [backlog|todo|in_progress|done] --sort asc
15053
+
15054
+ # Task operations
15055
+ ./.juno_task/scripts/kanban.sh get {TASK_ID}
15056
+ ./.juno_task/scripts/kanban.sh mark [in_progress|done|todo] --id {TASK_ID} --response "message"
15057
+ ./.juno_task/scripts/kanban.sh update {TASK_ID} --commit {COMMIT_HASH}
15058
+ \`\`\`
15059
+
15060
+ When a task on kanban, has related_tasks key, you need to get the task to understand the complete picture of tasks related to the current current task, you can get all the context through
15061
+ \`./.juno_task/scripts/kanban.sh get {TASK_ID}\`
15062
+
15063
+
15064
+
15028
15065
  ## Build & Test Commands
15029
15066
 
15030
15067
  **Environment Setup:**
@@ -24307,7 +24344,7 @@ function handleCLIError(error, verbose = false) {
24307
24344
  process.exit(EXIT_CODES.UNEXPECTED_ERROR);
24308
24345
  }
24309
24346
  function setupGlobalOptions(program) {
24310
- program.option("-v, --verbose", "Enable verbose output with detailed progress").option("-q, --quiet", "Disable rich formatting, use plain text").option("-c, --config <path>", "Configuration file path (.json, .toml, pyproject.toml)").option("-l, --log-file <path>", "Log file path (auto-generated if not specified)").option("--no-color", "Disable colored output").option("--log-level <level>", "Log level for output (error, warn, info, debug, trace)", "info").option("-s, --subagent <name>", "Subagent to use (claude, cursor, codex, gemini)").option("-b, --backend <type>", "Backend to use (mcp, shell)").option("-m, --model <name>", "Model to use (subagent-specific)").option("--agents <config>", "Agents configuration (forwarded to shell backend, ignored for MCP)").option("--tools <tools...>", 'Specify the list of available tools from the built-in set (only works with --print mode). Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read"). Passed to shell backend, ignored for MCP.').option("--allowed-tools <tools...>", 'Permission-based filtering of specific tool instances (e.g. "Bash(git:*) Edit"). Default when not specified: Task, Bash, Glob, Grep, ExitPlanMode, Read, Edit, Write, NotebookEdit, WebFetch, TodoWrite, WebSearch, BashOutput, KillShell, Skill, SlashCommand, EnterPlanMode. Passed to shell backend, ignored for MCP.').option("--disallowed-tools <tools...>", "Disallowed tools for Claude (passed to shell backend, ignored for MCP). By default, no tools are disallowed").option("--append-allowed-tools <tools...>", "Append tools to the default allowed-tools list (mutually exclusive with --allowed-tools). Passed to shell backend, ignored for MCP.").option("--mcp-timeout <number>", "MCP server timeout in milliseconds", parseInt).option("--enable-feedback", "Enable interactive feedback mode (F+Enter to enter, Q+Enter to submit)").option("-r, --resume <sessionId>", "Resume a conversation by session ID (shell backend only)").option("--continue", "Continue the most recent conversation (shell backend only)");
24347
+ program.option("-v, --verbose", "Enable verbose output with detailed progress").option("-q, --quiet", "Disable rich formatting, use plain text").option("-c, --config <path>", "Configuration file path (.json, .toml, pyproject.toml)").option("-l, --log-file <path>", "Log file path (auto-generated if not specified)").option("--no-color", "Disable colored output").option("--log-level <level>", "Log level for output (error, warn, info, debug, trace)", "info").option("-s, --subagent <name>", "Subagent to use (claude, cursor, codex, gemini)").option("-b, --backend <type>", "Backend to use (mcp, shell) - default: shell").option("-m, --model <name>", "Model to use (subagent-specific)").option("--agents <config>", "Agents configuration (forwarded to shell backend, ignored for MCP)").option("--tools <tools...>", 'Specify the list of available tools from the built-in set (only works with --print mode). Use "" to disable all tools, "default" to use all tools, or specify tool names (e.g. "Bash,Edit,Read"). Passed to shell backend, ignored for MCP.').option("--allowed-tools <tools...>", 'Permission-based filtering of specific tool instances (e.g. "Bash(git:*) Edit"). Default when not specified: Task, Bash, Glob, Grep, ExitPlanMode, Read, Edit, Write, NotebookEdit, WebFetch, TodoWrite, WebSearch, BashOutput, KillShell, Skill, SlashCommand, EnterPlanMode. Passed to shell backend, ignored for MCP.').option("--disallowed-tools <tools...>", "Disallowed tools for Claude (passed to shell backend, ignored for MCP). By default, no tools are disallowed").option("--append-allowed-tools <tools...>", "Append tools to the default allowed-tools list (mutually exclusive with --allowed-tools). Passed to shell backend, ignored for MCP.").option("--mcp-timeout <number>", "MCP server timeout in milliseconds", parseInt).option("--enable-feedback", "Enable interactive feedback mode (F+Enter to enter, Q+Enter to submit)").option("-r, --resume <sessionId>", "Resume a conversation by session ID (shell backend only)").option("--continue", "Continue the most recent conversation (shell backend only)").option("--til-completion", "Run juno-code in a loop until all kanban tasks are complete (aliases: --until-completion, --run-until-completion, --till-complete)").option("--until-completion", "Alias for --til-completion").addOption(new commander.Option("--run-until-completion", "Alias for --til-completion").hideHelp()).addOption(new commander.Option("--till-complete", "Alias for --til-completion").hideHelp()).option("--pre-run-hook <hooks...>", "Execute named hooks from .juno_task/config.json before each iteration (only with --til-completion)");
24311
24348
  program.exitOverride((err) => {
24312
24349
  if (err.code === "commander.helpDisplayed") {
24313
24350
  process.exit(0);
@@ -24332,6 +24369,61 @@ function setupMainCommand(program) {
24332
24369
  Object.entries(globalOptions).filter(([_, v]) => v !== void 0)
24333
24370
  );
24334
24371
  const allOptions2 = { ...definedGlobalOptions, ...options };
24372
+ if (allOptions2.tilCompletion || allOptions2.untilCompletion || allOptions2.runUntilCompletion || allOptions2.tillComplete) {
24373
+ const { spawn: spawn4 } = await import('child_process');
24374
+ const path24 = await import('path');
24375
+ const fs22 = await import('fs-extra');
24376
+ const scriptPath = path24.join(process.cwd(), ".juno_task", "scripts", "run_until_completion.sh");
24377
+ if (!await fs22.pathExists(scriptPath)) {
24378
+ console.error(chalk15__default.default.red.bold("\n\u274C Error: run_until_completion.sh not found"));
24379
+ console.error(chalk15__default.default.red(` Expected location: ${scriptPath}`));
24380
+ console.error(chalk15__default.default.yellow('\n\u{1F4A1} Suggestion: Run "juno-code init" to initialize the project'));
24381
+ process.exit(1);
24382
+ }
24383
+ const scriptArgs = [];
24384
+ if (allOptions2.preRunHook && Array.isArray(allOptions2.preRunHook)) {
24385
+ for (const hook of allOptions2.preRunHook) {
24386
+ scriptArgs.push("--pre-run-hook", hook);
24387
+ }
24388
+ }
24389
+ const completionFlags = ["--til-completion", "--until-completion", "--run-until-completion", "--till-complete"];
24390
+ const forwardedArgs = process.argv.slice(2).filter(
24391
+ (arg) => !completionFlags.includes(arg) && !arg.startsWith("--pre-run-hook")
24392
+ );
24393
+ scriptArgs.push(...forwardedArgs);
24394
+ const child = spawn4(scriptPath, scriptArgs, {
24395
+ stdio: "inherit",
24396
+ cwd: process.cwd()
24397
+ });
24398
+ process.removeAllListeners("SIGINT");
24399
+ process.removeAllListeners("SIGTERM");
24400
+ let childExited = false;
24401
+ const signalHandler = (signal) => {
24402
+ if (!childExited && child.pid) {
24403
+ try {
24404
+ process.kill(child.pid, signal);
24405
+ } catch (err) {
24406
+ }
24407
+ }
24408
+ };
24409
+ process.on("SIGINT", signalHandler);
24410
+ process.on("SIGTERM", signalHandler);
24411
+ child.on("exit", (code) => {
24412
+ childExited = true;
24413
+ process.removeListener("SIGINT", signalHandler);
24414
+ process.removeListener("SIGTERM", signalHandler);
24415
+ process.exit(code || 0);
24416
+ });
24417
+ child.on("error", (error) => {
24418
+ childExited = true;
24419
+ process.removeListener("SIGINT", signalHandler);
24420
+ process.removeListener("SIGTERM", signalHandler);
24421
+ console.error(chalk15__default.default.red.bold("\n\u274C Error executing run_until_completion.sh"));
24422
+ console.error(chalk15__default.default.red(` ${error.message}`));
24423
+ process.exit(1);
24424
+ });
24425
+ return;
24426
+ }
24335
24427
  if (!globalOptions.subagent && !options.prompt && !options.interactive && !options.interactivePrompt) {
24336
24428
  const fs22 = await import('fs-extra');
24337
24429
  const path24 = await import('path');
@@ -24572,9 +24664,12 @@ ${chalk15__default.default.blue.bold("\u{1F3AF} Juno Code")} - TypeScript CLI fo
24572
24664
  `);
24573
24665
  program.addHelpText("afterAll", `
24574
24666
  ${chalk15__default.default.blue.bold("Examples:")}
24575
- ${chalk15__default.default.gray("# Initialize new project")}
24667
+ ${chalk15__default.default.gray("# Initialize new project (interactive mode)")}
24576
24668
  juno-code init
24577
24669
 
24670
+ ${chalk15__default.default.gray("# Initialize with inline mode (automation-friendly)")}
24671
+ juno-code init "Build a REST API" --subagent claude --git-repo https://github.com/user/repo
24672
+
24578
24673
  ${chalk15__default.default.gray("# Start execution using .juno_task/init.md")}
24579
24674
  juno-code start
24580
24675
 
@@ -24604,7 +24699,7 @@ ${chalk15__default.default.blue.bold("Examples:")}
24604
24699
  juno-code config create development
24605
24700
 
24606
24701
  ${chalk15__default.default.gray("# Setup Git repository")}
24607
- juno-code setup-git https://github.com/owner/repo
24702
+ juno-code setup-git https://github.com/askbudi/juno-code
24608
24703
 
24609
24704
  ${chalk15__default.default.blue.bold("Environment Variables:")}
24610
24705
  JUNO_CODE_SUBAGENT Default subagent (claude, cursor, codex, gemini)
@@ -24627,8 +24722,9 @@ ${chalk15__default.default.blue.bold("Configuration:")}
24627
24722
  4. Built-in defaults (lowest priority)
24628
24723
 
24629
24724
  ${chalk15__default.default.blue.bold("Support:")}
24630
- Documentation: https://github.com/owner/juno-code#readme
24631
- Issues: https://github.com/owner/juno-code/issues
24725
+ Documentation: https://github.com/askbudi/juno-code#readme
24726
+ Issues: https://github.com/askbudi/juno-code/issues
24727
+ Website: https://askbudi.ai
24632
24728
  License: MIT
24633
24729
 
24634
24730
  `);