Skip to content

Commit fdae926

Browse files
authored
Merge pull request #192 from htilly/develop
Merge develop → master: Add AI Code Agent
2 parents f3b0409 + 922394c commit fdae926

17 files changed

Lines changed: 1437 additions & 60 deletions

.github/agent/agent.js

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { execSync } from "child_process";
2+
import OpenAI from "openai";
3+
import fs from "fs";
4+
5+
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
6+
const task = process.env.TASK || "Improve code quality";
7+
const requester = process.env.REQUESTER || "unknown";
8+
9+
function sh(cmd) {
10+
return execSync(cmd, { encoding: "utf8", stdio: "pipe" });
11+
}
12+
13+
console.log(`[AGENT] Starting AI code agent for task: ${task}`);
14+
console.log(`[AGENT] Requested by: ${requester}`);
15+
16+
// Get repo context
17+
const files = sh("git ls-files");
18+
const recentCommits = sh("git log --oneline -10");
19+
20+
// Read cursor rules for context
21+
let cursorRules = "";
22+
try {
23+
cursorRules = fs.readFileSync(".cursorrules", "utf8");
24+
} catch (e) {
25+
console.log("[AGENT] No .cursorrules file found");
26+
}
27+
28+
// Build specialized prompt for SlackONOS
29+
const prompt = `You are an autonomous coding agent for SlackONOS, a democratic music bot for Discord and Slack that controls Sonos speakers.
30+
31+
CRITICAL SAFETY RULES:
32+
- Output ONLY a valid unified git diff (starting with "diff --git")
33+
- DO NOT modify authentication files (webauthn-handler.js, auth-handler.js)
34+
- DO NOT modify config handling (config/*)
35+
- DO NOT modify security-critical code
36+
- Small, focused changes only (max 300 lines changed)
37+
- Follow existing code style (CommonJS, async/await, logger for logging)
38+
- NEVER use console.log in production code, use logger instead
39+
- Test your changes mentally before outputting the diff
40+
41+
CODEBASE CONTEXT:
42+
43+
Project Rules and Conventions:
44+
${cursorRules}
45+
46+
Repository Files:
47+
${files}
48+
49+
Recent Commits:
50+
${recentCommits}
51+
52+
TASK FROM ADMIN (${requester}):
53+
${task}
54+
55+
Generate a safe, focused code change as a unified git diff. The diff will be applied with "git apply" so ensure it's properly formatted.
56+
57+
Remember: Output ONLY the git diff, no explanations, no markdown code blocks, just the raw diff.`;
58+
59+
console.log("[AGENT] Calling OpenAI API...");
60+
const res = await client.chat.completions.create({
61+
model: "gpt-4o",
62+
temperature: 0.2,
63+
messages: [{ role: "user", content: prompt }],
64+
});
65+
66+
const output = res.choices[0].message.content;
67+
68+
// Extract diff from potential markdown code blocks
69+
let diff = output;
70+
if (output.includes("```")) {
71+
// Extract content between code fences
72+
const match = output.match(/```(?:diff)?\n([\s\S]*?)```/);
73+
if (match) {
74+
diff = match[1];
75+
}
76+
}
77+
78+
// Validate diff format
79+
if (!diff.includes("diff --git")) {
80+
console.error("[AGENT] Model did not return a valid diff");
81+
console.error("[AGENT] Output was:");
82+
console.error(output);
83+
process.exit(1);
84+
}
85+
86+
// Safety check: Ensure we're not touching forbidden files
87+
const forbiddenPatterns = [
88+
/webauthn-handler\.js/,
89+
/auth-handler\.js/,
90+
/config\/config\.json$/,
91+
/config\/userActions\.json/,
92+
/config\/webauthn-credentials\.json/
93+
];
94+
95+
for (const pattern of forbiddenPatterns) {
96+
if (pattern.test(diff)) {
97+
console.error(`[AGENT] SAFETY VIOLATION: Attempted to modify forbidden file matching ${pattern}`);
98+
process.exit(1);
99+
}
100+
}
101+
102+
// Count lines changed
103+
const linesChanged = (diff.match(/^[+-][^+-]/gm) || []).length;
104+
if (linesChanged > 300) {
105+
console.error(`[AGENT] SAFETY VIOLATION: Too many lines changed (${linesChanged} > 300)`);
106+
process.exit(1);
107+
}
108+
109+
console.log(`[AGENT] Generated diff with ${linesChanged} lines changed`);
110+
111+
// Apply patch
112+
fs.writeFileSync("/tmp/aicode.patch", diff);
113+
try {
114+
sh("git apply --check /tmp/aicode.patch");
115+
sh("git apply /tmp/aicode.patch");
116+
console.log("[AGENT] Patch applied successfully");
117+
} catch (err) {
118+
console.error("[AGENT] Failed to apply patch:", err.message);
119+
console.error("[AGENT] Diff was:");
120+
console.error(diff);
121+
process.exit(1);
122+
}
123+
124+
// Show diff for logs
125+
console.log("\n[AGENT] Generated changes:");
126+
console.log(sh("git diff"));
127+
128+
// Stage changes
129+
sh("git add -A");
130+
sh('git config user.name "AICODE Agent"');
131+
sh('git config user.email "aicode@slackonos.bot"');
132+
sh(`git commit -m "AI: ${task}"`);
133+
134+
console.log("[AGENT] Changes committed, ready for testing");
135+

.github/workflows/aicode-agent.yml

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
name: AICODE Agent
2+
3+
on:
4+
repository_dispatch:
5+
types: [aicode]
6+
7+
jobs:
8+
agent:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
contents: write
12+
pull-requests: write
13+
14+
steps:
15+
- name: Checkout develop
16+
uses: actions/checkout@v4
17+
with:
18+
ref: develop
19+
fetch-depth: 0
20+
21+
- name: Setup Node.js
22+
uses: actions/setup-node@v4
23+
with:
24+
node-version: 20
25+
cache: 'npm'
26+
27+
- name: Install dependencies
28+
run: npm ci
29+
30+
- name: Run AI Agent
31+
env:
32+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
33+
TASK: ${{ github.event.client_payload.task }}
34+
REQUESTER: ${{ github.event.client_payload.requester }}
35+
run: node .github/agent/agent.js
36+
37+
- name: Run tests
38+
id: tests
39+
run: npm test
40+
continue-on-error: true
41+
42+
- name: Create PR if tests pass
43+
id: cpr
44+
if: steps.tests.outcome == 'success'
45+
uses: peter-evans/create-pull-request@v6
46+
with:
47+
token: ${{ secrets.GITHUB_TOKEN }}
48+
commit-message: "AI: ${{ github.event.client_payload.task }}"
49+
branch: aicode-${{ github.run_number }}
50+
base: develop
51+
title: "AICODE: ${{ github.event.client_payload.task }}"
52+
body: |
53+
Autonomous code changes requested by @${{ github.event.client_payload.requester }}
54+
55+
**Task:** ${{ github.event.client_payload.task }}
56+
**Tests:** ✅ Passed
57+
**Agent Run:** ${{ github.run_id }}
58+
59+
Review the changes carefully before merging.
60+
61+
- name: Notify Slack on success
62+
if: steps.tests.outcome == 'success'
63+
run: |
64+
curl -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \
65+
-H "Content-Type: application/json" \
66+
-d "{\"text\":\"✅ AICODE PR created: <https://github.com/htilly/SlackONOS/pull/${{ steps.cpr.outputs.pull-request-number }}|#${{ steps.cpr.outputs.pull-request-number }}>\"}"
67+
68+
- name: Notify Slack on failure
69+
if: steps.tests.outcome != 'success'
70+
run: |
71+
curl -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" \
72+
-H "Content-Type: application/json" \
73+
-d "{\"text\":\"❌ AICODE failed: Tests didn't pass. <https://github.com/htilly/SlackONOS/actions/runs/${{ github.run_id }}|View logs>\"}"
74+

.gitignore

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,51 @@
11
# ===================================================================
2-
# Project Coverage
2+
# Dependencies
3+
# ===================================================================
4+
node_modules/
5+
6+
# ===================================================================
7+
# Coverage & Build Artifacts
38
# ===================================================================
49
coverage/
510

611
# ===================================================================
7-
# Dependencies
12+
# Logs
813
# ===================================================================
9-
node_modules/
14+
*.log
1015

1116
# ===================================================================
12-
# Editor & Workspace Settings
17+
# Editor / IDE Files
1318
# ===================================================================
1419
.vscode/
15-
*.swp
16-
*.swo
17-
*.swn
18-
19-
# Cursor editor files
2020
.claude/
2121
.cursor/
2222
.cursorrules/
2323

24+
# Vim swap files
25+
*.swp
26+
*.swo
27+
*.swn
2428

2529
# ===================================================================
26-
# Logs
30+
# OS Files
2731
# ===================================================================
28-
*.log
32+
.DS_Store
33+
Thumbs.db
2934

3035
# ===================================================================
31-
# Configuration Files
36+
# Configuration & Secrets
3237
# ===================================================================
33-
config/config.json
34-
config/userActions.json
35-
config/webauthn-credentials.json
38+
config/*.json
3639
config/ssl/
40+
ssl/
3741

3842
# ===================================================================
3943
# Docker
4044
# ===================================================================
41-
docker-compose.yml
4245
docker-compose.override.yml
4346

4447
# ===================================================================
4548
# Test Artifacts
4649
# ===================================================================
4750
test/config/test-config.json
4851
test/timing-log.json
49-
50-
# ===================================================================
51-
# OS Files
52-
# ===================================================================
53-
.DS_Store
54-
Thumbs.db

0 commit comments

Comments
 (0)