Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: use workflows-ai under the hood #67

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
20 changes: 19 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,25 @@ module.exports = {
},
],
'object-shorthand': ['error', 'always'],
'simple-import-sort/imports': 'error',
'simple-import-sort/imports': [
'error',
{
groups: [
// Node.js builtins
['^node:'],
// External packages
['^@?\\w'],
// Internal packages and cali-tools
['^cali-tools'],
// Parent imports
['^\\.\\.(?!/?$)', '^\\.\\./?$'],
// Other relative imports
['^\\./(?=.*/)(?!/?$)', '^\\.(?!/?$)', '^\\./?$'],
// Style imports
['^.+\\.s?css$'],
],
},
],
'simple-import-sort/exports': 'error',
},
overrides: [
Expand Down
Binary file modified bun.lockb
Binary file not shown.
38 changes: 0 additions & 38 deletions docs/tools.md

This file was deleted.

10 changes: 2 additions & 8 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"devDependencies": {
"@release-it-plugins/workspaces": "^4.2.0",
"@release-it/conventional-changelog": "^9.0.3",
"@rslib/core": "^0.1.1",
"@typescript-eslint/eslint-plugin": "^7.3.1",
"@typescript-eslint/parser": "^7.3.1",
"bun-types": "^1.1.33",
Expand All @@ -15,20 +14,15 @@
"eslint-plugin-simple-import-sort": "^12.0.0",
"prettier": "^3.2.5",
"release-it": "^17.10.0",
"tsup": "^8.3.5",
"tsx": "^4.19.2",
"typescript": "^5.1.3",
"vitest": "^2.1.1"
},
"license": "MIT",
"patchedDependencies": {
"[email protected]": "patches/[email protected]"
},
"private": true,
"scripts": {
"build": "bun run build:tools && bun run build:mcp-server && bun run build:cli",
"build:tools": "cd packages/tools && bun run build",
"build:mcp-server": "cd packages/mcp-server && bun run build",
"build:cli": "cd packages/cali && bun run build",
"build": "bun run --filter '*' build",
"release": "release-it"
},
"workspaces": [
Expand Down
7 changes: 4 additions & 3 deletions packages/cali/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
},
"type": "module",
"scripts": {
"build": "rslib build",
"dev": "node --import=tsx ./src/cli.ts",
"start": "node ./dist/index.js"
"prepare": "bun run build",
"clean": "rm -rf dist",
"build": "tsup-node"
},
"dependencies": {
"@ai-sdk/openai": "^1.0.2",
"@clack/prompts": "^0.8.1",
"ai": "^4.0.3",
"ai-flows": "^0.1.2",
"cali-tools": "0.3.1",
"chalk": "^5.3.0",
"dedent": "^1.5.3",
Expand Down
33 changes: 0 additions & 33 deletions packages/cali/rslib.config.ts

This file was deleted.

175 changes: 175 additions & 0 deletions packages/cali/src/agents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import { openai } from '@ai-sdk/openai'
import { confirm, select, text } from '@clack/prompts'
import { tool } from 'ai'
import { agent } from 'ai-flows'
import { z } from 'zod'

/**
* Tools
*/
import * as androidTools from 'cali-tools/android'
import * as appleTools from 'cali-tools/apple'
import { getReactNativeConfig, startMetroDevServer } from 'cali-tools/react-native'

/**
* Helper tool to throw errors when something wents wrong on the tool level.
*/
export const somethingWentWrong = tool({
description:
'Call this tool when something went wrong and you cannot return what you were asked for',
parameters: z.object({
error: z
.string()
.describe('Error message with details and potential recovery steps to display to the user'),
}),
execute: async ({ error }): Promise<string> => {
throw new Error(error)
},
})

const askUserFromList = tool({
description: 'Ask the user to choose one of the provided options',
parameters: z.object({
question: z.string(),
options: z.array(
z.object({
value: z.string().describe('The value of the option'),
label: z.string().describe('The label that explains the value'),
})
),
}),
execute: async ({ question, options }) => {
const answer = await select({
message: question,
options,
})
if (typeof answer !== 'string') {
throw new Error('User cancelled the operation')
}
return answer
},
})

const askUserOpenEndedQuestion = tool({
description: 'Ask the user to answer a question',
parameters: z.object({
question: z.string(),
}),
execute: async ({ question }) => {
const answer = await text({
message: question,
})
if (typeof answer !== 'string') {
throw new Error('User cancelled the operation')
}
return answer
},
})

const confirmWithUser = tool({
description: 'Ask the user to confirm something (yes/no)',
parameters: z.object({
question: z.string(),
}),
execute: async ({ question }) => {
const answer = await confirm({
message: question,
})
if (typeof answer !== 'boolean') {
throw new Error('User cancelled the operation')
}
return answer
},
})

const userInputTools = {
askUserOpenEndedQuestion,
askUserFromList,
confirmWithUser,
}

/**
* Agent that ask the user for input.
*/
export const userInputAgent = agent({
system: `
Your job is to ask the user for input and return his answer as string.
You choose the right tool to ask the user for input, depending on the type of question.
If you are given multiple questions, you must ask them one by one, and return all answers.
You must return only user answers, without any unnecessary commentary.
You must include all the details in the answer.
`,
model: openai('gpt-4o'),
tools: userInputTools,
})

export const reactNativeAgent = agent({
system: `
You are a helpful assistant that helps with everything related to React Native.

You do not know what platforms are available.
You must run a tool to list available platforms.

If platform is already specified in context, you must use it.
If platform is not specified in context, you must ask the user to choose one and include chosen platform in the final response.
Use that platform for all operations.
`,
model: openai('gpt-4o'),
tools: {
getReactNativeConfig,
startMetroDevServer,
...userInputTools,
},
})

export const appleAgent = agent({
system: `
You are a helpful assistant that helps with everything related to iOS.

When running an app on simulator, you must first check if the simulator is running.
If it is not, you must start it.

If there are multiple simulators or devices available, you must ask the user to choose one first.
Do not ask the user if information is already provided in context.
`,
model: openai('gpt-4o'),
tools: {
...appleTools,
...userInputTools,
},
})

export const androidAgent = agent({
system: `
You are a helpful assistant that helps with everything related to Android.

When running an app on emulator, you must first check if the emulator is running.
If it is not, you must start it.

If there are multiple emulators or devices available, you must ask the user to choose one first.
Do not ask the user if information is already provided in context.
`,
model: openai('gpt-4o'),
tools: {
...androidTools,
...userInputTools,
},
})

export const processAgent = agent({
system: `
You are a helpful assistant that helps with everything related to process.
You are given a command to execute.
You must execute the command and return the result.
`,
model: openai('gpt-4o'),
tools: {
exit: tool({
description: 'Exit the application',
parameters: z.object({}),
execute: () => {
process.exit(0)
},
}),
},
})
Loading