Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

Orchestrate complex, long-running coding tasks to an ephemeral cloud environment integrated with a GitHub repo.

## Examples

- [Advanced Session](./examples/advanced-session/README.md)
- [Webhook Integration](./examples/webhook/README.md)
- [GitHub Actions](./examples/github-actions/README.md)

## Send work to a Cloud based session

```ts
Expand Down
14 changes: 14 additions & 0 deletions packages/core/examples/advanced-session/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Advanced Session Example

This example demonstrates how to use the Jules SDK to run a session, interact with it, wait for plan approvals, and monitor stream progress while parsing the output for artifacts.

## Running

Ensure you have your `JULES_API_KEY` set.
You can run the script via:

```sh
bun run index.ts
```

This will run the advanced session and stream activities.
81 changes: 81 additions & 0 deletions packages/core/examples/advanced-session/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { jules, JulesError } from '@google/jules-sdk';

/**
* Advanced Session Example
*
* Demonstrates:
* - Interactive session creation
* - Waiting for plan approval
* - Monitoring progress via reactive streams
* - Handling code diffs (changeSet)
*/
async function runAdvancedSession() {
try {
console.log('Starting advanced session...');

// Interactive Sessions allow you to provide feedback and guide the process
const session = await jules.session({
prompt: `Create a simple python script that prints 'Hello Advanced Session!' and test it.`,
// For a repoless session, we don't provide a source
});

console.log(`Session created: ${session.id}`);

// Wait for the plan to be generated and ready for approval
console.log('Waiting for plan approval...');
await session.waitFor('awaitingPlanApproval');

console.log('Plan is ready. Approving it now.');
await session.approve();

// Stream activities and artifacts
for await (const activity of session.stream()) {
switch (activity.type) {
case 'planGenerated':
console.log(
'Plan:',
activity.plan?.steps.map((s) => s.title)
);
break;
case 'agentMessaged':
console.log('Agent says:', activity.message);
break;
case 'progressUpdated':
console.log(`Progress: ${activity.title}`);
break;
case 'sessionCompleted':
console.log('Session complete!');
break;
}

// Check artifacts
for (const artifact of activity.artifacts ?? []) {
if (artifact.type === 'bashOutput') {
console.log(`[BASH] ${artifact.toString()}`);
}
if (artifact.type === 'changeSet') {
const parsed = artifact.parsed();
for (const file of parsed.files) {
console.log(
`[DIFF] ${file.path}: +${file.additions} -${file.deletions}`
);
}
}
}
}

const outcome = await session.result();
console.log(`Session finished with state: ${outcome.state}`);

} catch (error) {
if (error instanceof JulesError) {
console.error(`SDK error: ${error.message}`);
} else {
console.error('Unknown error:', error);
}
}
}

if (require.main === module) {
runAdvancedSession();
}
59 changes: 59 additions & 0 deletions packages/core/examples/github-actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# GitHub Actions Example

This example demonstrates how to use the Jules SDK within a GitHub Action to automate workflows.

## Overview

This example creates a custom GitHub Action that:
1. Takes a `prompt` input.
2. Reads the `JULES_API_KEY` from the environment.
3. Automatically uses the current repository and branch as context.
4. Starts a Jules session to perform the task.
5. Monitors the progress and outputs the resulting Pull Request URL.

## Files

- `index.ts`: The main action logic using the Jules SDK.
- `action.yml`: The metadata file that defines the action inputs, outputs, and runtime.
- `package.json`: Dependencies for the action (`@actions/core`, `@actions/github`, `@google/jules-sdk`).

## Usage

You can use this action in your own GitHub workflows by referencing its path or publishing it.

Here is an example workflow file (`.github/workflows/jules.yml`) that triggers the agent whenever an issue is labeled `agent-fix`:

```yaml
name: Jules Agent Workflow

on:
issues:
types: [labeled]

jobs:
run-jules:
if: github.event.label.name == 'agent-fix'
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Run Jules Agent
uses: ./packages/core/examples/github-actions # Path to where this action is located
env:
JULES_API_KEY: ${{ secrets.JULES_API_KEY }}
with:
prompt: "Fix the following issue: ${{ github.event.issue.title }}\n\n${{ github.event.issue.body }}"
```

## Running the Example Locally

To compile the TypeScript action:

```bash
cd packages/core/examples/github-actions
npm install
npm run build
```

The compiled JavaScript will be placed in `dist/index.js`, which is what GitHub Actions will execute.
13 changes: 13 additions & 0 deletions packages/core/examples/github-actions/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: 'Jules Agent Action'
description: 'Run a Jules coding agent session on your repository'
author: 'Google'
inputs:
prompt:
description: 'The instructions for the Jules agent'
required: true
outputs:
pr-url:
description: 'The URL of the Pull Request created by the agent, if any'
runs:
using: 'node20'
main: 'dist/index.js'
89 changes: 89 additions & 0 deletions packages/core/examples/github-actions/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as core from '@actions/core';
import * as github from '@actions/github';
import { jules } from '@google/jules-sdk';

async function run() {
try {
// 1. Read inputs defined in action.yml
const prompt = core.getInput('prompt', { required: true });

// 2. The Jules SDK requires JULES_API_KEY environment variable.
// GitHub Actions can pass this via `env:` or `with:` which sets an input.
// If you prefer not to use the environment variable, you can initialize
// the client specifically:
// const julesClient = jules.with({ apiKey: core.getInput('api-key') });

// We will assume JULES_API_KEY is available in the environment,
// which is the default behavior of the `jules` instance.
if (!process.env.JULES_API_KEY) {
throw new Error('JULES_API_KEY environment variable is missing.');
}

// 3. Get context about the current repository from the GitHub Action payload
const context = github.context;
const owner = context.repo.owner;
const repo = context.repo.repo;
const ref = context.ref;

// Convert refs/heads/main to main
let baseBranch = 'main';
if (ref.startsWith('refs/heads/')) {
baseBranch = ref.replace('refs/heads/', '');
}

core.info(`Starting Jules session for ${owner}/${repo} on branch ${baseBranch}`);
core.info(`Prompt: ${prompt}`);

// 4. Create a new Jules session
const session = await jules.session({
prompt: prompt,
source: {
github: `${owner}/${repo}`,
baseBranch: baseBranch,
},
// Automatically create a PR when the session is complete
autoPr: true,
});

core.info(`Session created successfully. ID: ${session.id}`);

// 5. Monitor the progress
for await (const activity of session.stream()) {
switch (activity.type) {
case 'planGenerated':
core.info(`[Plan Generated] ${activity.plan.steps.length} steps.`);
break;
case 'progressUpdated':
core.info(`[Progress Updated] ${activity.title}`);
break;
case 'sessionCompleted':
core.info(`[Session Completed]`);
break;
}
}

// 6. Wait for the final outcome
const outcome = await session.result();

if (outcome.state === 'failed') {
core.setFailed(`Session failed.`);
return;
}

core.info(`Session finished successfully.`);

if (outcome.pullRequest) {
core.info(`Pull Request created: ${outcome.pullRequest.url}`);
// Set an output that other steps in the workflow can use
core.setOutput('pr-url', outcome.pullRequest.url);
}
} catch (error) {
if (error instanceof Error) {
core.setFailed(`Action failed with error: ${error.message}`);
} else {
core.setFailed(`Action failed with an unknown error.`);
}
}
}

run();
18 changes: 18 additions & 0 deletions packages/core/examples/github-actions/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "jules-github-actions-example",
"version": "1.0.0",
"description": "An example of using the Jules SDK in a GitHub Action",
"main": "dist/index.js",
"scripts": {
"build": "tsc"
},
"dependencies": {
"@actions/core": "^1.10.1",
"@actions/github": "^6.0.0",
"@google/jules-sdk": "workspace:*"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.0.0"
}
}
13 changes: 13 additions & 0 deletions packages/core/examples/github-actions/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "node",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"outDir": "./dist"
},
"include": ["index.ts"]
}
36 changes: 36 additions & 0 deletions packages/core/examples/webhook/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Webhook Integration Example

This example demonstrates how to create a simple webhook server using [Hono](https://hono.dev/) that listens for incoming HTTP `POST` requests and automatically creates a new Jules session.

This is particularly useful for event-driven workflows, such as automatically running an agent when a specific event occurs in another system (e.g., a GitHub issue being created, a new row added to a database, or a custom application event).

## Prerequisites

- Ensure you have [Bun](https://bun.sh/) installed, or another compatible runtime like Node.js.
- Ensure you have your `JULES_API_KEY` set as an environment variable.

## Running the Example

1. Start the webhook server:

```bash
bun install
bun run index.ts
```

2. The server will start and listen on port `3000`.

3. Send a test webhook payload using `curl` (or your preferred tool like Postman):

```bash
curl -X POST http://localhost:3000/webhook \
-H "Content-Type: application/json" \
-d '{"event": "bug_report", "description": "Fix typo in README"}'
```

4. Check the server console logs. You should see the incoming payload and the ID of the newly created Jules session.

## Notes

- In a real-world scenario, you should validate the incoming webhook payload to ensure it comes from a trusted source (e.g., verifying a signature or secret token).
- You can customize the `prompt` and `source` in `index.ts` to match your specific requirements and target repository.
42 changes: 42 additions & 0 deletions packages/core/examples/webhook/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { serve } from '@hono/node-server';
import { Hono } from 'hono';
import { jules } from '@google/jules-sdk';

const app = new Hono();

app.post('/webhook', async (c) => {
try {
const payload = await c.req.json();
console.log('Received webhook payload:', payload);

// Create a new Jules session triggered by the webhook
const session = await jules.session({
prompt: `Process this webhook payload: ${JSON.stringify(payload)}`,
// Provide a default repository or adapt to your needs
source: { github: 'davideast/dataprompt', baseBranch: 'main' },
});

console.log(`Created Jules session: ${session.id}`);

return c.json({
success: true,
message: 'Webhook processed and session created successfully',
sessionId: session.id,
}, 200);

} catch (error) {
console.error('Error processing webhook:', error);
return c.json({
success: false,
message: 'Failed to process webhook',
}, 500);
}
});

const port = 3000;
console.log(`Server is running on port ${port}`);

serve({
fetch: app.fetch,
port,
});
Loading
Loading