acdev 1.0.0

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.
@@ -0,0 +1,13 @@
1
+ # Optional. Prefer Claude Code subscription auth instead:
2
+ # claude auth login
3
+ # Or a long-lived subscription token:
4
+ # claude setup-token
5
+ # CLAUDE_CODE_OAUTH_TOKEN=...
6
+ #
7
+ # API Console billing (pay-as-you-go) — only if you have an Anthropic API key:
8
+ # ANTHROPIC_API_KEY=sk-ant-your-key-here
9
+ #
10
+ # Jira Cloud (only when ticketSource is "jira" in Settings / config.json):
11
+ # JIRA_BASE_URL=https://your-domain.atlassian.net
12
+ # JIRA_EMAIL=you@company.com
13
+ # JIRA_API_TOKEN=your-atlassian-api-token
package/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # acdev
2
+
3
+ Local CLI + web UI for running AI coding agents on GitHub Issues or Jira tickets via isolated git worktrees. Pull requests are always created on the local repo’s GitHub remote with `gh`.
4
+
5
+ ## Prerequisites
6
+
7
+ - Node.js 20+
8
+ - [GitHub CLI](https://cli.github.com/) (`gh`) authenticated (`gh auth login`) — required for PRs (and for GitHub Issues when that is the ticket source)
9
+ - Claude authentication (see [Authentication](#authentication) below)
10
+ - A git repository with a `develop` branch on `origin` (or configure another base branch)
11
+ - Optional: Jira Cloud credentials when using Jira as the ticket source (see [Jira](#jira-ticket-source))
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ npm i -g @giangarcia14/acdev
17
+ # or
18
+ npx @giangarcia14/acdev
19
+ ```
20
+
21
+ From a local clone (development):
22
+
23
+ ```bash
24
+ npm install
25
+ npm link # optional — makes `acdev` available globally
26
+ ```
27
+
28
+ ## Authentication
29
+
30
+ `acdev` uses the [Claude Agent SDK](https://platform.claude.com/docs/en/agent-sdk/overview), which shares credentials with [Claude Code](https://code.claude.com/docs/en/authentication). You need **one** of the following (`.env` is optional):
31
+
32
+ ### Option A — Claude Pro / Max subscription (browser login)
33
+
34
+ Best when you already pay for claude.ai and are not allowed to use API keys.
35
+
36
+ 1. Install [Claude Code](https://code.claude.com/docs/en/setup) so the `claude` CLI is on your `PATH`
37
+ 2. Sign in with your Claude.ai account:
38
+
39
+ ```bash
40
+ claude auth login
41
+ # verify:
42
+ claude auth status
43
+ ```
44
+
45
+ 3. Run `acdev` with **no** `ANTHROPIC_API_KEY` set (an API key would take precedence and bill the Console instead of your subscription)
46
+
47
+ For CI / non-interactive shells, generate a long-lived subscription token instead:
48
+
49
+ ```bash
50
+ claude setup-token
51
+ export CLAUDE_CODE_OAUTH_TOKEN=... # or put it in `.acdev/.env`
52
+ ```
53
+
54
+ ### Option B — Anthropic API key (Console / pay-as-you-go)
55
+
56
+ ```bash
57
+ cp .acdev/.env.example .acdev/.env
58
+ # edit .acdev/.env and set ANTHROPIC_API_KEY=sk-ant-...
59
+ acdev
60
+ ```
61
+
62
+ Or export the key in your shell (overrides `.env` if both are set):
63
+
64
+ ```bash
65
+ export ANTHROPIC_API_KEY=sk-ant-...
66
+ acdev
67
+ ```
68
+
69
+ A Claude.ai browser-only subscription does **not** give you an API key. API keys come from the [Claude Console](https://platform.claude.com/) and are billed separately from Pro/Max.
70
+
71
+ ## Jira ticket source
72
+
73
+ Tickets can come from **GitHub Issues** or **Jira Cloud**. Switch in **Settings → Ticket source**. The server still requires `gh` auth (PRs); Jira credentials are only required when enqueueing Jira issues.
74
+
75
+ ### Connect
76
+
77
+ 1. Create an [Atlassian API token](https://id.atlassian.com/manage-profile/security/api-tokens)
78
+ 2. In **Settings**, choose **Jira**, then set:
79
+ - Base URL — `https://your-domain.atlassian.net`
80
+ - Email — Atlassian account email
81
+ - API token
82
+ 3. Click **Test connection** (`GET /rest/api/3/myself`), then **Save settings**
83
+
84
+ Secrets are written to `.acdev/.env` (gitignored):
85
+
86
+ ```bash
87
+ JIRA_BASE_URL=https://your-domain.atlassian.net
88
+ JIRA_EMAIL=you@company.com
89
+ JIRA_API_TOKEN=...
90
+ ```
91
+
92
+ Non-secret settings (`ticketSource`, `jiraBaseUrl`, `jiraPrLinkPhrase`) live in `.acdev/config.json`.
93
+
94
+ ### Enqueue
95
+
96
+ Paste Jira keys (`PROJ-123`) or browse URLs (`https://…atlassian.net/browse/PROJ-123`). PR bodies use `Relates to PROJ-123` (configurable) instead of `Closes #N`. Approving still pushes the branch and runs `gh pr create`.
97
+
98
+ Optional **Rules** in Settings can move the Jira ticket to a target status (e.g. `In Review`) after a PR opens, via a best-effort workflow transition. Failures are logged as warnings and never undo a successful PR.
99
+
100
+ **Limitations:** Jira Cloud REST API v3 only (not Server/Data Center). PRs always target GitHub.
101
+
102
+ ## Usage
103
+
104
+ From inside a cloned git repository (after authenticating as above):
105
+
106
+ ```bash
107
+ acdev
108
+ ```
109
+
110
+ The tool starts a local server (default port `4848`), opens the browser, and shows the web UI.
111
+
112
+ ### CLI flags
113
+
114
+ | Flag | Description |
115
+ |------|-------------|
116
+ | `--port <n>` | Override server port (also `ACDEV_PORT`) |
117
+ | `--base-branch <name>` | Override base branch for this run only |
118
+ | `--model <id>` | Override Claude model for this run (e.g. `claude-sonnet-5`) |
119
+ | `--no-open` | Skip automatic browser open |
120
+ | `--stub-agent` | Use a 2s stub agent (no API key needed; for testing UI flow) |
121
+
122
+ ### Workflow
123
+
124
+ 1. Paste GitHub issue URLs or Jira keys/URLs on **Overview** and click **Add to queue**
125
+ 2. Jobs process sequentially: sync base branch → create worktree → run agent → await review
126
+ 3. Monitor live logs on **Runs**; review diffs on **Review**
127
+ 4. **Approve as draft** (draft PR) or **Approve as ready for review** (non-draft PR), or **Reject** (cleanup worktree, keep history as discarded)
128
+ 5. **Clear** removes a job from history entirely (so you can re-queue the same issue). Allowed for queued / awaiting review / PR opened / failed / discarded; refused while a job is in-flight
129
+
130
+ ### Web UI views
131
+
132
+ | View | Purpose |
133
+ |------|---------|
134
+ | **Overview** | Add issue URLs / Jira keys, see queued jobs and metrics; Clear queued jobs |
135
+ | **Runs** | Active / recent jobs with live SSE logs and progress steps; Clear terminal jobs |
136
+ | **Review** | Edit PR title/body, colored diff, approve draft or ready, reject, or Clear |
137
+ | **Logs** | Cross-job activity log with job filter |
138
+ | **Alerts** | Failures / warnings with Retry, Clear, and dismiss |
139
+ | **Settings** | Ticket source (GitHub / Jira), Jira connection, post-PR rules, and `.acdev/config.json` fields |
140
+
141
+ Model selection lives in the sidebar (and Settings). Default is **Sonnet 5** (`claude-sonnet-5`).
142
+
143
+ ## Configuration
144
+
145
+ Optional secrets go in `.acdev/.env` (copy from `.acdev/.env.example`). The file is gitignored and not required when using `claude auth login` with GitHub Issues only.
146
+
147
+ Prefer **Settings** in the web UI to change runtime config — values persist to `.acdev/config.json` and apply to the next job without restart. You can still edit the file by hand if you prefer.
148
+
149
+ On first run, creates `.acdev/config.json`:
150
+
151
+ ```json
152
+ {
153
+ "baseBranch": "develop",
154
+ "testCommand": null,
155
+ "maxAgentTurns": 30,
156
+ "allowedTools": ["Read", "Glob", "Grep", "Edit", "Write", "Bash"],
157
+ "agentTimeoutMs": 900000,
158
+ "model": "claude-sonnet-5",
159
+ "ticketSource": "github",
160
+ "jiraBaseUrl": "",
161
+ "jiraPrLinkPhrase": "Relates to",
162
+ "jiraRules": {
163
+ "afterPrOpened": { "enabled": false, "targetStatus": "In Review" }
164
+ },
165
+ "githubRules": {
166
+ "afterPrOpened": { "enabled": false, "action": "none", "label": "" }
167
+ }
168
+ }
169
+ ```
170
+
171
+ `jiraRules.afterPrOpened` transitions the issue to a workflow status whose name matches `targetStatus` (case-insensitive). `githubRules.afterPrOpened.action` is `none`, `add_label`, or `close_issue` (GitHub issues have no built-in board statuses; Projects API is not used).
172
+
173
+ Supported `model` values (Claude Agent SDK):
174
+
175
+ | UI label | Model ID |
176
+ |----------|----------|
177
+ | Sonnet 5 (default) | `claude-sonnet-5` |
178
+ | Opus 5 | `claude-opus-5` |
179
+
180
+ Unknown or removed model IDs in an existing config are migrated to `claude-sonnet-5` on load.
181
+
182
+ Change via **Settings** / the sidebar model toggle (`PATCH /api/config`) or `--model`. Takes effect for the next agent run without restart.
183
+
184
+ Job state is persisted in `.acdev/state.json`.
185
+
186
+ If you previously used **codepilot** or **agent-mcp**, first run migrates `.codepilot/` or `.agent-mcp/` → `.acdev/` (and matching `-worktrees/` dirs → `.acdev-worktrees/` when present) with a one-line console notice. Prefers `.codepilot/` when both legacy dirs exist.
187
+
188
+ ## Tests
189
+
190
+ ```bash
191
+ npm test
192
+ ```
193
+
194
+ ## Architecture
195
+
196
+ - `bin/acdev.js` — CLI entry, startup checks (`gh` always; Jira not required at startup)
197
+ - `src/claude-auth.js` — Anthropic API key / Claude Code subscription auth detection
198
+ - `src/server.js` — Express API, SSE logs, job queue, config + Jira test endpoints
199
+ - `src/git.js` — Worktree lifecycle (`issue-<id>` paths), `feat/`/`fix/` branch naming, diff, push
200
+ - `src/agent.js` — Claude Agent SDK integration (`options.model`); GitHub vs Jira prompts
201
+ - `src/github.js` — Issue URL parsing, issue fetch via `gh`, `createPr({ draft })`
202
+ - `src/jira.js` — Jira Cloud parse/fetch/test + issuetype → feat/fix
203
+ - `src/config.js` — Load/save `.acdev/config.json` including ticket source
204
+ - `src/env.js` — Load/upsert `.acdev/.env` (Anthropic + Jira secrets)
205
+ - `src/paths.js` — Data/worktree paths + legacy `.codepilot` / `.agent-mcp` migration
206
+ - `src/store.js` — JSON persistence
207
+ - `public/` — Vanilla HTML/JS/CSS UI (no build step)
208
+
209
+ ### API (selected)
210
+
211
+ | Method | Path | Notes |
212
+ |--------|------|-------|
213
+ | `GET` | `/api/jobs` | All jobs |
214
+ | `POST` | `/api/issues` | `{ urls: string[], ticketSource?: 'github'\|'jira' }` — enqueue |
215
+ | `GET` | `/api/jobs/:id/events` | SSE log stream |
216
+ | `POST` | `/api/jobs/:id/approve` | `{ prTitle, prBody, draft?: boolean }` — default draft `true` |
217
+ | `POST` | `/api/jobs/:id/reject` | Discard worktree (keeps job as `discarded`) |
218
+ | `DELETE` | `/api/jobs/:id` | Clear job from history (worktree best-effort); 409 if in-flight |
219
+ | `POST` | `/api/jobs/:id/retry` | Re-queue failed/discarded |
220
+ | `GET` | `/api/config` | Editable config + models + Jira status (token masked) |
221
+ | `PATCH` | `/api/config` | Partial update including `ticketSource`, `jiraBaseUrl`; `jiraEmail` / `jiraApiToken` → `.env` |
222
+ | `POST` | `/api/jira/test` | Test Jira credentials (`GET /rest/api/3/myself`) |
223
+
224
+ ### Branch naming
225
+
226
+ Before the agent runs, acdev fetches the ticket and creates a worktree branch:
227
+
228
+ - **GitHub:** Bug labels / title keywords → `fix/<slug>`; feature labels → `feat/<slug>`; unclear → `fix/<slug>`
229
+ - **Jira:** issuetype Bug → `fix/<slug>`; Story / Feature / Task / Improvement → `feat/<slug>`; else `fix/<slug>`
230
+
231
+ Slugs are lowercase, hyphenated, and truncated (~50 chars). If the branch already exists, `-<issueNumber-or-key>` is appended. Worktree directories remain `issue-<N-or-key>` for uniqueness.
package/bin/acdev.js ADDED
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { execSync } from 'node:child_process';
4
+ import http from 'node:http';
5
+ import open from 'open';
6
+ import { checkClaudeAuth } from '../src/claude-auth.js';
7
+ import { checkGhAuth } from '../src/gh-auth.js';
8
+ import { loadConfig } from '../src/config.js';
9
+ import { loadEnv } from '../src/env.js';
10
+ import { migrateLegacyDataDir, migrateLegacyWorktreesDir } from '../src/paths.js';
11
+ import { Store } from '../src/store.js';
12
+ import { createServer, listen } from '../src/server.js';
13
+
14
+ function parseArgs(argv) {
15
+ const opts = {
16
+ port: Number(process.env.ACDEV_PORT) || 4848,
17
+ baseBranch: null,
18
+ model: null,
19
+ noOpen: false,
20
+ stubAgent: process.env.ACDEV_STUB === '1',
21
+ };
22
+
23
+ for (let i = 2; i < argv.length; i++) {
24
+ const arg = argv[i];
25
+ if (arg === '--port') {
26
+ opts.port = Number(argv[++i]);
27
+ } else if (arg === '--base-branch') {
28
+ opts.baseBranch = argv[++i];
29
+ } else if (arg === '--model') {
30
+ opts.model = argv[++i];
31
+ } else if (arg === '--no-open') {
32
+ opts.noOpen = true;
33
+ } else if (arg === '--stub-agent') {
34
+ opts.stubAgent = true;
35
+ }
36
+ }
37
+
38
+ return opts;
39
+ }
40
+
41
+ async function main() {
42
+ const opts = parseArgs(process.argv);
43
+
44
+ let repoRoot;
45
+ try {
46
+ repoRoot = execSync('git rev-parse --show-toplevel', {
47
+ encoding: 'utf8',
48
+ cwd: process.cwd(),
49
+ }).trim();
50
+ } catch {
51
+ console.error(
52
+ '✖ Not inside a git repository. cd into your repo and re-run `acdev`.'
53
+ );
54
+ process.exit(1);
55
+ }
56
+
57
+ migrateLegacyDataDir(repoRoot);
58
+ migrateLegacyWorktreesDir(repoRoot);
59
+ loadEnv(repoRoot);
60
+
61
+ const ghAuth = checkGhAuth();
62
+ if (!ghAuth.ok) {
63
+ if (ghAuth.reason === 'not-found') {
64
+ console.error(
65
+ '✖ GitHub CLI (gh) not found in PATH. Install it from https://cli.github.com/'
66
+ );
67
+ } else {
68
+ console.error(
69
+ '✖ GitHub CLI not authenticated for github.com. Run `gh auth login -h github.com` first.'
70
+ );
71
+ }
72
+ process.exit(1);
73
+ }
74
+
75
+ if (!opts.stubAgent) {
76
+ const claudeAuth = checkClaudeAuth();
77
+ if (!claudeAuth.ok) {
78
+ console.error(
79
+ [
80
+ '✖ No Anthropic / Claude Code authentication found.',
81
+ '',
82
+ 'Authenticate with one of:',
83
+ ' 1. Claude Pro/Max subscription (browser): install Claude Code, then run',
84
+ ' `claude auth login` (or `claude setup-token` and set CLAUDE_CODE_OAUTH_TOKEN)',
85
+ ' 2. Anthropic API key (API billing): set ANTHROPIC_API_KEY in the environment',
86
+ ' or in `.acdev/.env` (optional file; copy from `.acdev/.env.example`)',
87
+ '',
88
+ 'For UI-only testing without auth: pass `--stub-agent`.',
89
+ ].join('\n')
90
+ );
91
+ process.exit(1);
92
+ }
93
+ }
94
+
95
+ const config = loadConfig(repoRoot);
96
+ if (opts.baseBranch) {
97
+ config.baseBranch = opts.baseBranch;
98
+ }
99
+ if (opts.model) {
100
+ config.model = opts.model;
101
+ }
102
+
103
+ const store = new Store(repoRoot);
104
+ store.reconcileStaleJobs();
105
+
106
+ const { app, processQueue } = createServer({
107
+ repoRoot,
108
+ config,
109
+ store,
110
+ useStubAgent: opts.stubAgent,
111
+ });
112
+
113
+ const server = http.createServer(app);
114
+
115
+ try {
116
+ await listen(server, opts.port);
117
+ } catch (err) {
118
+ console.error(`✖ ${err.message}`);
119
+ process.exit(1);
120
+ }
121
+
122
+ console.log(`✓ acdev running at http://localhost:${opts.port}`);
123
+
124
+ if (!opts.noOpen) {
125
+ try {
126
+ await open(`http://localhost:${opts.port}`);
127
+ } catch {
128
+ // headless — URL already printed
129
+ }
130
+ }
131
+
132
+ processQueue().catch((err) => console.error('[acdev] processQueue failed:', err));
133
+ }
134
+
135
+ main().catch((err) => {
136
+ console.error(err);
137
+ process.exit(1);
138
+ });
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "acdev",
3
+ "version": "1.0.0",
4
+ "description": "Local CLI + web UI for running AI agents on GitHub issues via git worktrees",
5
+ "type": "module",
6
+ "bin": {
7
+ "acdev": "./bin/acdev.js"
8
+ },
9
+ "files": [
10
+ "bin",
11
+ "src",
12
+ "public",
13
+ "README.md",
14
+ ".acdev/.env.example"
15
+ ],
16
+ "engines": {
17
+ "node": ">=20"
18
+ },
19
+ "scripts": {
20
+ "test": "node --test test/*.test.js",
21
+ "start": "node bin/acdev.js"
22
+ },
23
+ "dependencies": {
24
+ "@anthropic-ai/claude-agent-sdk": "0.1.77",
25
+ "dotenv": "^17.4.2",
26
+ "express": "^4.21.2",
27
+ "open": "^10.1.0",
28
+ "simple-git": "^3.27.0",
29
+ "uuid": "^11.1.0"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/SftwreDev/agent-mcp.git"
37
+ },
38
+ "keywords": [
39
+ "agent",
40
+ "acdev",
41
+ "claude",
42
+ "github",
43
+ "cli"
44
+ ],
45
+ "author": "",
46
+ "license": "ISC",
47
+ "bugs": {
48
+ "url": "https://github.com/SftwreDev/agent-mcp/issues"
49
+ },
50
+ "homepage": "https://github.com/SftwreDev/agent-mcp#readme",
51
+ "main": "index.js",
52
+ "directories": {
53
+ "test": "test"
54
+ },
55
+ "devDependencies": {}
56
+ }
@@ -0,0 +1,10 @@
1
+ <svg width="100%" viewBox="0 0 680 220" role="img" xmlns="http://www.w3.org/2000/svg" style="">
2
+ <title style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:anthropic-sans, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">Acdev wordmark logo</title>
3
+ <desc style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:anthropic-sans, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">A minimalist geometric text-only wordmark reading "acdev" styled as a terminal prompt, with a leading chevron in purple</desc>
4
+
5
+ <g transform="translate(140,140)" style="fill:rgb(0, 0, 0);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:anthropic-sans, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto">
6
+ <text x="0" y="0" font-size="72" style="fill:rgb(108, 92, 231);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:&quot;Courier New&quot;, monospace;font-size:72px;font-weight:700;text-anchor:start;dominant-baseline:auto">&gt;</text>
7
+ <text x="58" y="0" font-size="72" style="fill:rgb(22, 24, 29);stroke:none;color:rgb(11, 11, 11);stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:Arial, sans-serif;font-size:72px;font-weight:700;text-anchor:start;dominant-baseline:auto">acdev</text>
8
+ </g>
9
+ <line x1="140" y1="168" x2="400" y2="168" stroke-width="3" style="fill:rgb(0, 0, 0);stroke:rgb(108, 92, 231);color:rgb(11, 11, 11);stroke-width:3px;stroke-linecap:butt;stroke-linejoin:miter;opacity:1;font-family:anthropic-sans, -apple-system, &quot;system-ui&quot;, &quot;Segoe UI&quot;, sans-serif;font-size:16px;font-weight:400;text-anchor:start;dominant-baseline:auto"/>
10
+ </svg>