# DevIntern: Full Documentation > DevIntern is an agentic development suite. `@devintern/pm` writes engineering specs across PM tools; `@devintern/code` implements Jira tickets end-to-end using your AI agent of choice. # Code # Quick Start **@devintern/code** is your AI intern for automatically implementing tasks from your task tracker. It fetches task details, creates feature branches, runs your AI agent, commits changes, and optionally creates pull requests, all from a single command. Supported task trackers today: **Jira** (default), **Linear**, **Trello**, **Asana**, **Azure DevOps**, **GitHub Issues**, and **local markdown files** (no PM account required). ## Prerequisites - **[Bun](https://bun.sh) runtime** (required to run @devintern/code) - Task tracker account with API access (Jira, Linear, Trello, Asana, Azure DevOps, or GitHub Issues), or local markdown files with no account at all - AI agent CLI installed (e.g., Claude Code, OpenCode, Codex, Cursor) - Git repository for your project ## Installation Install globally with Bun: ```bash # Install Bun if not already installed curl -fsSL https://bun.sh/install | bash # Install @devintern/code globally bun install -g @getdevintern/code ``` ## Initialize Configuration Navigate to your project directory and run: ```bash devintern init ``` In a terminal, this starts an interactive setup wizard that: - Detects an existing @devintern/pm configuration (`.devintern-pm/.env`) in the same project and offers to reuse those tracker credentials, so you skip straight to validation - Asks which task tracker you use (Jira, Linear, GitHub Issues, Azure DevOps, Asana, Trello, or markdown files) - Links you directly to the provider's token creation page and prompts for each credential, with a pointer to the matching setup guide in these docs - Validates the connection with a real API call before finishing (you can retry, edit values, or skip) - Offers an optional GitHub token for pull request creation - Writes your answers to `.devintern-code/.env`, creates `settings.json` for per-project configuration, and adds `.devintern-code/.env` to your `.gitignore` (to prevent leaking credentials) For scripted or CI setups, pass `--yes` (or `--no-interactive`) to skip the prompts and write a commented configuration template instead: ```bash devintern init --yes ``` ## Connect Your Task Tracker The wizard handles credentials for you. If you used `--yes`, or want to change trackers later, edit `.devintern-code/.env` for the tracker you use. Optionally edit `.devintern-code/settings.json` for status or list transitions after a run. | Tracker | When to use | Setup guide | | ------------------ | --------------------------------------------------------- | ----------------------------------------------------------------- | | **Jira** (default) | Jira Cloud issues, JQL batch runs, story point estimation | [Jira Integration](/docs/code/jira-integration) | | **Linear** | Linear issues by ID or URL, IssueFilter batch runs | [Linear Integration](/docs/code/linear-integration) | | **Trello** | Trello cards by short link or URL | [Trello Integration](/docs/code/trello-integration) | | **Asana** | Asana tasks with project section transitions | [Asana Integration](/docs/code/asana-integration) | | **Azure DevOps** | Azure DevOps work items by ID or URL | [Azure DevOps Integration](/docs/code/azure-devops-integration) | | **GitHub Issues** | GitHub issues with status labels, PRs in the same repo | [GitHub Issues Integration](/docs/code/github-issues-integration) | | **Markdown files** | Local `.md` specs, no PM account needed | [Markdown File Tasks](/docs/code/markdown-tasks) | **Jira:** add `JIRA_BASE_URL`, `JIRA_EMAIL`, and `JIRA_API_TOKEN`. You do not need to set `TASK_TRACKER` (it defaults to `jira`). **Trello:** set `TASK_TRACKER=trello` plus `TRELLO_API_KEY` and `TRELLO_API_TOKEN`. Shared options (GitHub/Bitbucket PRs, agent harness, output directory) are covered in [Configuration](/docs/code/configuration). ## First Run Run `devintern` with a task reference from your configured tracker: **Jira** ```bash devintern PROJ-123 --create-pr ``` **Trello** (`TASK_TRACKER=trello` in `.devintern-code/.env`) ```bash devintern AbCdEf12 --create-pr ``` Every run: 1. **Fetches** task details (description, comments, attachments where supported) 2. **Transitions** the task (Jira status or Trello list, when configured in `settings.json`) 3. **Creates** a feature branch (`feature/proj-123`) 4. **Runs** a clarity check (skippable with `--skip-clarity-check`) 5. **Executes** your AI agent with formatted task context 6. **Commits** changes automatically after successful implementation 7. **Posts** a summary back to your task tracker ## What's Next? - [Usage](/docs/code/usage): CLI flags, query-based batch runs, git, and pull requests - [Automated task processing](/docs/code/automated-task-processing): scheduled runs with systemd or cron --- # Configuration @devintern/code uses per-project configuration stored in `.devintern-code/.env` in your project directory. This allows you to work with multiple projects without configuration conflicts. The easiest way to create this file is `devintern init`. In a terminal it runs an interactive wizard: pick your tracker, follow the deep link to the provider's token page, paste the credentials, and the wizard verifies the connection before writing `.env`. Pass `--yes` (or `--no-interactive`) to skip the prompts and generate a commented template to fill in by hand, which is also what happens automatically when stdin is not a terminal (CI, scripts). ## Environment File Locations The tool searches for `.env` files in the following order: 1. **Custom path** (if specified with `--env-file`) 2. **Project discovery**: traverses up from the current working directory, checking `.devintern-code/.env` then `.env` at each level, stopping at the first `.git` root found or your home directory 3. **User home directory** (`~/.env`) 4. **Tool installation directory** You can run `devintern` from any subdirectory of your project and it will find the correct config automatically. ## Required Configuration The active task tracker is set with `TASK_TRACKER` (defaults to `jira`). Supported values: `jira`, `linear`, `trello`, `asana`, `azure-devops`, `github`, `markdown`. ### Jira (default) Update your `.env` file with your Jira credentials: ```bash JIRA_BASE_URL=https://yourcompany.atlassian.net JIRA_EMAIL=your-email@company.com JIRA_API_TOKEN=your-api-token ``` Get your Jira API token at [https://id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens) See the [Jira Integration guide](/docs/code/jira-integration) for setup, JQL batch runs, and troubleshooting. ### Linear Set `TASK_TRACKER=linear` and provide a Personal API key: ```bash TASK_TRACKER=linear LINEAR_API_KEY=lin_api_xxxxxxxxxxxx ``` Create a Personal API key at [https://linear.app/settings/api](https://linear.app/settings/api). Story points are written to Linear's built-in `estimate` field, so no custom field ID is required. See the [Linear Integration guide](/docs/code/linear-integration) for state transitions, JSON `IssueFilter` batch runs, and cron examples. ### Trello Set `TASK_TRACKER=trello` and provide Power-Up credentials: ```bash TASK_TRACKER=trello TRELLO_API_KEY=your-power-up-api-key TRELLO_API_TOKEN=your-user-token TRELLO_DEFAULT_BOARD_ID=abc123 # optional: board short ID from the board URL ``` See the [Trello Integration guide](/docs/code/trello-integration) for token generation and list transition setup. ### Markdown (local files) To use local `.md` files as tasks without any PM credentials, set the tracker to `markdown` and point it at your tasks directory: ```bash TASK_TRACKER=markdown MARKDOWN_TASKS_DIR=/path/to/tasks ``` `MARKDOWN_TASKS_DIR` is required when `TASK_TRACKER=markdown`. `devintern` resolves bare task keys (e.g. `my-feature`) to `{MARKDOWN_TASKS_DIR}/my-feature.md`. You can also pass file paths directly as arguments without setting `TASK_TRACKER=markdown` at all. In that mode no `.devintern-code/.env` is needed for tracker credentials. See the [Markdown File Tasks guide](/docs/code/markdown-tasks) for details. ## Optional PR Integration Choose one authentication method for pull request creation: ### GitHub Personal Access Token For individual users: ```bash GITHUB_TOKEN=your-github-token ``` - **Classic token**: Requires `repo` scope - **Fine-grained token** (recommended): Requires `Pull requests: Read and write` and `Contents: Read` permissions - Create at: [https://github.com/settings/tokens](https://github.com/settings/tokens) ### GitHub App Authentication For organizations: ```bash GITHUB_APP_ID=123456 GITHUB_APP_PRIVATE_KEY_PATH=/secure/path/to/your-app.private-key.pem ``` **Benefits:** - No individual tokens needed - Fine-grained permissions - Centralized control - Audit trail **Setup steps:** 1. Go to **Settings → Developer settings → GitHub Apps → New GitHub App** 2. Set repository permissions: - **Contents:** Read - **Pull requests:** Read and write 3. Generate and save a private key 4. Install the App on your repositories > These permissions cover task implementation and PR creation. If you also run the webhook server to auto-address PR feedback, that App needs additional **Pull request review comments** and **Issue comments** permissions plus event subscriptions; see [GitHub Integration](/docs/code/github-integration#update-app-permissions). For CI/CD environments, you can use a base64-encoded key: ```bash GITHUB_APP_ID=123456 GITHUB_APP_PRIVATE_KEY_BASE64=LS0tLS1CRUdJTi4uLg== ``` To encode your key: ```bash # macOS base64 -i your-app.private-key.pem # Linux base64 -w 0 your-app.private-key.pem ``` ### Bitbucket ```bash BITBUCKET_TOKEN=your-bitbucket-token ``` Requires `Repositories: Write` permission. Create at [https://bitbucket.org/account/settings/app-passwords/](https://bitbucket.org/account/settings/app-passwords/) ## Per-Project Settings The `.devintern-code/settings.json` file allows project-specific behavior. Settings are organized by task tracker, so you can configure multiple trackers in one file. ```json { "jira": { "projects": { "PROJ": { "prStatus": "In Review", "inProgressStatus": "In Progress", "todoStatus": "To Do", "storyPointsField": "customfield_10016" } } }, "linear": { "projects": { "ENG": { "prStatus": "In Review", "inProgressStatus": "In Progress", "todoStatus": "Backlog" } } }, "trello": { "projects": { "abc123": { "prStatus": "Review", "inProgressStatus": "Doing", "todoStatus": "To Do" } } } } ``` The active tracker is read from the `TASK_TRACKER` environment variable (defaults to `jira`). Run `devintern init` to generate a `settings.json` with examples for all supported trackers. **Fields (all optional):** - `prStatus`: Status/state/label to transition to after PR creation (e.g., "In Review") - `inProgressStatus`: Status to set when starting work (e.g., "In Progress", "Doing") - `todoStatus`: Status to reset to if implementation fails (e.g., "To Do", "Backlog") - `storyPointsField`: Custom field ID for story points (e.g., `"customfield_10016"` for Jira); auto-discovered if omitted **Supported trackers:** `jira`, `linear`, `trello`, `asana`, `azure-devops`, `github`, `markdown`. **Backward compatibility:** Existing Jira-only files using the legacy top-level `projects` key continue to work without any changes. ```json { "projects": { "PROJ": { "prStatus": "In Review" } } } ``` ## Verbose API Logging To enable detailed API call logging for debugging, set the `DEVINTERN_VERBOSE` environment variable: ```bash DEVINTERN_VERBOSE=1 ``` This logs every API request, response, and retry attempt to the console. Leave it unset (the default) for quiet operation. ## Output Directory By default, task artifacts are saved to `/tmp/devintern-tasks`. You can customize this: ```bash DEVINTERN_OUTPUT_DIR=./devintern-output ``` **Output structure:** ``` {output-dir}/{task-key}/ ├── task-details.md # Formatted task for AI agent ├── feasibility-assessment.md # Clarity check results ├── implementation-summary.md # Success output ├── implementation-summary-incomplete.md # Failure output ├── auto-review-summary.json # Auto-review loop results ├── iteration-{N}/ # Auto-review iterations │ ├── feedback.json │ └── review-prompt.txt └── attachments/ # Jira attachments ``` ## Agent Harness Configure which AI agent runs and how long it can work: ```bash # Agent harness type (default: claude-code) AGENT_HARNESS=claude-code # Optional: path to the agent CLI (leave unset in most cases) # AGENT_CLI_PATH=/custom/path/to/claude ``` You usually only need `AGENT_HARNESS`. By default devintern uses the harness's standard command (for example `claude` for `claude-code`) and finds it on your `PATH` automatically, so `AGENT_CLI_PATH` can be left unset. Common `AGENT_HARNESS` values include `claude-code`, `opencode`, `codex`, `cursor`, `grok`, `deepseek`, `antigravity`, `cline`, `goose`, `kilo-code`, `kimi`, and `qwen`. If you do need to set a path explicitly, run `which` for the harness binary (`claude`, `opencode`, `codex`, `cursor-agent`, `grok`, `reasonix`, `agy`, `cline`, `goose`, `kilo`, `kimi`, or `qwen`). **Cursor note:** The Cursor harness uses Cursor's headless `cursor-agent` CLI (not a command named `cursor`). Cursor also installs an `agent` alias, but devintern looks for `cursor-agent` because other tools use the `agent` name too. Install Cursor and enable the CLI from Cursor's settings, then set `AGENT_HARNESS=cursor`. The `--max-turns` option has no effect with this harness; Cursor runs until the task is complete. **Grok note:** Product name is Grok Build; the CLI binary is `grok`. Install from [x.ai/cli](https://x.ai/cli), authenticate (browser login or `XAI_API_KEY`), then set `AGENT_HARNESS=grok`. `--max-turns` has no effect; Grok runs until the task completes. **DeepSeek note:** Harness id is `deepseek`; the CLI binary is `reasonix` (DeepSeek-Reasonix, listed in DeepSeek's agent integrations). Install with `npm i -g reasonix`, set `DEEPSEEK_API_KEY` (or run `reasonix setup`), then set `AGENT_HARNESS=deepseek`. `--max-turns` and permission-skip flags have no effect on `reasonix run` (turn limits live in Reasonix config; headless runs are already autonomous). **Antigravity note:** Harness id is `antigravity` (alias `agy`); the CLI binary is `agy`. Google retired consumer Gemini CLI on 2026-06-18 in favor of Antigravity CLI. Install from [antigravity.google/docs/cli/install](https://antigravity.google/docs/cli/install), authenticate (browser/keyring, or `ANTIGRAVITY_TOKEN` for CI), then set `AGENT_HARNESS=antigravity`. Legacy `AGENT_HARNESS=gemini` still routes to Antigravity with a deprecation warning; DevIntern does not spawn the retired `gemini` binary. Prefer `AGENT_CLI_PATH` / `ANTIGRAVITY_CLI_PATH` / `AGY_CLI_PATH` over `GEMINI_CLI_PATH`. `--max-turns` has no effect; model selection is via Antigravity settings/`/model`, not a DevIntern model flag. **Kilo Code note:** Harness id is `kilo-code`; the CLI binary is `kilo`. **Qwen note:** Qwen Code has no `--model` flag; pick the model in `~/.qwen/settings.json`. Set `AGENT_CLI_PATH` only when the CLI is not on your `PATH` or uses a non-standard name. You can give it a bare command name or a full path. Avoid committing an **absolute** path to a shared `.env`: it is machine-specific, so copying an `.env` from macOS (`/Users/...`) to a Linux host (`/home/...`) would point at a non-existent binary. If the configured command cannot be found, devintern fails fast at startup with a message telling you the CLI is not on your `PATH`. **Advanced spawn tuning** (rarely needed): ```bash # Retry attempts when the agent CLI path is momentarily missing (e.g. during an auto-update) # Default: 5 AGENT_SPAWN_ENOENT_RETRIES=5 # Initial backoff delay in milliseconds between retries (doubles each attempt) # Default: 1000 AGENT_SPAWN_ENOENT_BACKOFF_MS=1000 ``` These control how devintern handles a brief window where the agent CLI symlink is missing because the tool is updating itself. The defaults are sufficient for all common auto-updaters. **CLI options:** - `--agent-path`: override the agent executable path - `--max-turns`: max turns for **implementation** (default: **500**; clarity checks always use 10) - `--skip-clarity-check`: skip feasibility assessment before implementation DevIntern always runs the full workflow after fetching a task (clarity check → agent → commit/PR). There is no fetch-only mode. ## Troubleshooting **"Missing required environment variables"** - Ensure `.env` file exists in `.devintern-code/` or your current directory - Check that variable names match exactly (case-sensitive) **"Not in a git repository"** - Run `git init` if starting a new project - Or use `--no-git` flag to skip git operations **"Claude CLI not found"** / **" CLI not found"** - Install your AI agent CLI and ensure it is on your `PATH` - Or specify path: `--agent-path /path/to/claude` (or set `AGENT_CLI_PATH`) **"Unknown agent harness"** - Check `AGENT_HARNESS` spelling (use kebab-case, e.g. `claude-code`, `grok`, `deepseek`) - The error lists every valid harness name; pick one from that list --- # Usage ## Single Task Processing Process a single task from your configured tracker: ```bash # Jira (default) devintern TASK-123 # Linear (TASK_TRACKER=linear) devintern ENG-42 # Trello (TASK_TRACKER=trello) devintern 4uWKPOTv devintern https://trello.com/c/4uWKPOTv/card-slug --create-pr # Local markdown file (no PM credentials required) devintern ./tasks/feature-spec.md devintern /path/to/my-task.md --create-pr # Skip git branch creation devintern TASK-123 --no-git # Use custom .env file devintern TASK-123 --env-file /path/to/custom.env # Verbose output for debugging devintern TASK-123 -v # Custom AI agent CLI path devintern TASK-123 --agent-path /path/to/claude # Override max turns for very complex tasks (default: 500) devintern TASK-123 --max-turns 1000 # Skip automatic commit after AI agent completes devintern TASK-123 --no-auto-commit # Create pull request after implementation devintern TASK-123 --create-pr # Create PR targeting specific branch devintern TASK-123 --create-pr --pr-target-branch develop # Skip ALL task tracker comments (and Trello list transitions) devintern TASK-123 --skip-comments # Skip clarity check for faster processing devintern TASK-123 --skip-clarity-check ``` ## Markdown File Tasks Pass one or more local `.md` files as arguments. No task tracker credentials are needed: ```bash # Single file devintern ./tasks/feature-spec.md --create-pr # Multiple files (processed in sequence) devintern ./epic.md ./subtask-a.md ./subtask-b.md # Mix a PM task with a local file devintern PROJ-123 ./extra-context.md ``` See the [Markdown File Tasks](/docs/code/markdown-tasks) guide for frontmatter options, status tracking, and `TASK_TRACKER=markdown` mode. ## Batch Processing Process multiple tasks at once. The query syntax depends on the active tracker; `--query` accepts JQL for Jira, and either a JSON `IssueFilter` or a plain-text title search for Linear. See the per-tracker integration guides for full details. ### Jira ```bash # Process multiple specific tasks devintern PROJ-123 PROJ-124 PROJ-125 # Process tasks matching a JQL query (Jira only; --jql is a deprecated alias) devintern --query "project = PROJ AND status = 'To Do'" # Complex JQL with custom fields devintern --query "project = \"My Project\" AND cf[10016] <= 3 AND labels IN (FrontEnd, MobileApp)" # Batch process with PR creation devintern --query "assignee = currentUser() AND status = 'To Do'" --create-pr # High-complexity batch with extended turns devintern --query "labels = 'refactoring' AND type = Story" --max-turns 1000 --create-pr # Batch with skipped clarity checks devintern PROJ-101 PROJ-102 PROJ-103 --skip-clarity-check --create-pr ``` ### Linear ```bash # Process multiple specific issues devintern ENG-42 ENG-43 ENG-44 # Process issues with a given label (e.g. "intern") devintern --query '{"labels":{"name":{"eq":"intern"}}}' --create-pr # Process "In Progress" issues assigned to me devintern --query '{"state":{"name":{"eq":"In Progress"}},"assignee":{"isMe":{"eq":true}}}' --create-pr # High-priority issues devintern --query '{"priority":{"lte":2}}' --create-pr ``` Wrap JSON filters in single quotes so the shell does not interpret the inner double quotes. See the [Linear Integration guide](/docs/code/linear-integration) for the full `IssueFilter` schema. ## Workflow Examples ### Standard Development Workflow ```bash # 1. Go to your project directory cd ~/projects/my-app # 2. Check git status (should be clean) git status # 3. Run devintern devintern MYAPP-456 # Expected output: # 🔍 Fetching JIRA task: MYAPP-456 # 📋 Task Summary: Implement user authentication # 💾 Saving formatted task details to: /tmp/devintern-tasks/myapp-456/task-details.md # 🌿 Creating feature branch... # ✅ Created and switched to new branch 'feature/myapp-456' # 🤖 Running Claude Code with task details... # [Agent implements the task...] # ✅ Agent execution completed successfully # 📝 Committing changes... # ✅ Successfully committed changes for MYAPP-456 ``` ## Git Integration Details ### Automatic Branch Creation - Creates branches with format: `feature/task-id` - Converts task keys to lowercase: `PROJ-123` → `feature/proj-123` - Checks for uncommitted changes before creating branches - Switches to existing branch if it already exists ### Automatic Commit - Commits all changes after AI agent successfully completes - Uses descriptive commit message: `feat: implement TASK-123 - Task Summary` - Can be disabled with `--no-auto-commit` flag - Skipped when the agent ends its run by asking you decision questions (for example "How should I proceed?" or a list of options). devintern prints the questions, posts them as a comment on the task, and stops without committing or opening a PR. Answer in the task and re-run. ### Pull Request Creation - Automatically creates PRs on GitHub or Bitbucket - Detects repository platform from git remote URL - PR title format: `[TASK-123] Task Summary` - PR body includes implementation details and links back to the task - Target branch can be specified with `--pr-target-branch` (defaults to `main`) - Target branch can also be auto-detected from the task description. Add a line like `Target branch: develop` to the card or issue and `devintern` will pick it up. Supported patterns: `Target branch:`, `Base branch:`, `PR target:`. Falls back to `--pr-target-branch` if no pattern matches. ## What It Does 1. **Fetches** task details (description, custom fields where supported, comments, attachments) 2. **Formats** the information for your AI agent 3. **Creates** a feature branch named `feature/task-id` 4. **Runs** optional feasibility assessment (skippable with `--skip-clarity-check`) 5. **Executes** your AI agent with enhanced permissions (default: 500 max turns) 6. **Saves** implementation summary to local files 7. **Commits** all changes automatically 8. **Pushes** the feature branch (when creating PRs) 9. **Creates** pull requests on GitHub or Bitbucket (optional) 10. **Posts** implementation results back to your task tracker (skippable with `--skip-comments`) ## Troubleshooting **"There are uncommitted changes"** - Commit your changes: `git add . && git commit -m "message"` - Or stash them: `git stash` - Or use `--no-git` to skip branch creation **"Agent reached maximum turns limit"** - Task is too complex for the current turn limit (default: 500) - Increase max turns: `--max-turns 1000` - Consider breaking the task into smaller subtasks **"PR creation failed"** - Ensure you have the correct token configured - Check token/App permissions - For GitHub App: Ensure the App is installed on the repository - Use `--verbose` flag to see detailed error messages **"Issue not found" / card fetch errors** - Check tracker credentials in `.devintern-code/.env` - Verify the task key or card ID exists and you have access - For Jira, ensure `JIRA_BASE_URL` is correct - For Trello, confirm `TASK_TRACKER=trello` and both `TRELLO_API_KEY` and `TRELLO_API_TOKEN` are set --- # Jira Integration @devintern/code implements work from Jira Cloud issues: fetch issue details, comments, and attachments, run a feasibility check, transition statuses, execute your AI agent, commit changes, open pull requests, and post results back to the issue. This integration targets **Jira Cloud** (`*.atlassian.net`). Jira Data Center / Server is not supported. ## How It Works @devintern/code uses the [Jira Cloud REST API](https://developer.atlassian.com/cloud/jira/platform/rest/v3/) to read and update issues you pass on the command line or select with JQL. - Issue descriptions are converted from **Atlassian Document Format (ADF)** to markdown for your AI agent - Comments from humans are included; prior @devintern/code comments are filtered out to reduce noise - Attachments are downloaded into the task output directory when present - Status transitions use workflow status **names** from your `settings.json` (for example, "In Progress", "In Review") - After a run, feasibility, implementation, or incomplete summaries are posted as Jira comments ## Setup ### 1. Initialize project config From your repository root: ```bash devintern init ``` This creates `.devintern-code/.env` and `.devintern-code/settings.json`. ### 2. Set Jira credentials In `.devintern-code/.env`: ```bash TASK_TRACKER=jira JIRA_BASE_URL=https://your-org.atlassian.net JIRA_EMAIL=your-email@example.com JIRA_API_TOKEN=your-api-token ``` `TASK_TRACKER` defaults to `jira` if omitted. All three Jira variables are required. ### 3. Create an API token 1. Go to [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens) 2. Click **Create API token**, add a label (e.g. `DevIntern`), and copy the token 3. Paste the token into `JIRA_API_TOKEN` Use the **email address** of the Atlassian account that owns the token for `JIRA_EMAIL`. Authentication is Basic auth (`email:token`). Store credentials in `.devintern-code/.env`. That file should stay out of version control (`devintern init` adds it to `.gitignore`). ### 4. Find your Jira site URL Your site URL is the hostname you use to open Jira in the browser: ``` https://your-org.atlassian.net/jira/... └─ JIRA_BASE_URL ─┘ ``` Set `JIRA_BASE_URL` to the full URL **without** a trailing slash. ### 5. Configure status transitions (optional) Edit `.devintern-code/settings.json` using the **project key** as the lookup key (the prefix in issue keys, e.g. `PROJ` in `PROJ-123`): ```json { "jira": { "projects": { "PROJ": { "inProgressStatus": "In Progress", "todoStatus": "To Do", "prStatus": "In Review", "storyPointsField": "customfield_10016" } } } } ``` @devintern/code transitions issues to: - **inProgressStatus** when implementation starts (after the clarity check) - **prStatus** after a pull request is created - **todoStatus** when implementation is incomplete or max turns are reached Status names must match your Jira workflow exactly. Omit a field to skip that transition. See [Configuration](/docs/code/configuration) for legacy `projects` format and other settings. ## Running tasks ### Single issue ```bash devintern PROJ-123 devintern PROJ-123 --create-pr devintern PROJ-123 --skip-clarity-check --create-pr ``` ### Multiple issues ```bash devintern PROJ-123 PROJ-124 PROJ-125 --create-pr ``` ### Batch with JQL ```bash devintern --jql "project = PROJ AND status = 'To Do'" --create-pr devintern --query "assignee = currentUser() AND status = 'To Do'" --create-pr ``` `--query` is the preferred flag; `--jql` remains available with a deprecation warning. ### Story points estimation Batch mode for AI-assisted estimation (also available for Linear): ```bash devintern PROJ-123 --estimate devintern --estimate --query "project = PROJ AND status = 'To Do'" ``` See [Story Points Estimation](/docs/code/story-points-estimation) for field discovery, re-estimation, and automation examples. ## What Gets Posted | Stage | Jira comment | | ------------------------- | -------------------------------------------------------- | | Feasibility check | Automated Task Feasibility Assessment (markdown summary) | | Successful implementation | Implementation Completed by @devintern/code | | Incomplete / max turns | Implementation Incomplete | | Estimation (`--estimate`) | Automated Story Points Estimation | Use `--skip-comments` to skip all Jira comments and status transitions for a run. ## Permissions Your Jira user needs, at minimum: - **Browse projects** and **View issues** on target projects - **Transition issues** if you configure status changes - **Add comments** unless you use `--skip-comments` - **Edit issues** for story points when using `--estimate` ## Troubleshooting **"Missing required JIRA environment variables"** Set `JIRA_BASE_URL`, `JIRA_EMAIL`, and `JIRA_API_TOKEN` in `.devintern-code/.env`. **"Issue not found"** - Verify the issue key exists and you have access - Confirm `JIRA_BASE_URL` matches your Cloud site - Check that `JIRA_EMAIL` matches the account that created the API token **Jira API error (401)** - API token is invalid or revoked. Create a new token at [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens) - `JIRA_EMAIL` does not match the token owner **Jira API error (403)** - Your account lacks permission on the issue or project - Ask a Jira admin to grant browse, comment, or transition permissions **Status transition failed** - The status name in `settings.json` does not match your workflow (check spelling and capitalization) - The transition may not be valid from the issue's current status - @devintern/code logs a warning and continues the run when a transition fails **JQL returns no issues** - Test the query in Jira's issue search first - Quote project names and statuses that contain spaces: `project = "My Project"` **Descriptions look wrong in agent context** @devintern/code converts ADF to markdown. Unusual ADF structures may lose formatting; simplify the issue description in Jira if needed. --- # Asana Integration @devintern/code can implement work directly from Asana tasks: fetch task details and comments, run a feasibility check, move the task between project sections, execute your AI agent, commit changes, open a pull request, and post results back as task comments. ## Prerequisites - [Bun](https://bun.sh) and `@getdevintern/code` installed globally - Asana personal access token - Git repository for your project ## Setup ### 1. Set the task tracker In `.devintern-code/.env`: ```bash TASK_TRACKER=asana ``` ### 2. Add Asana credentials ```bash ASANA_API_TOKEN=your-asana-pat ASANA_DEFAULT_PROJECT_GID=1200000000000000 ``` Create the personal access token in [Asana developer settings](https://app.asana.com/0/my-apps). `ASANA_DEFAULT_PROJECT_GID` is optional but recommended: it anchors section transitions and batch queries to a specific project. Find the GID in the project URL. ### 3. Configure section transitions Asana has no global workflow states, so @devintern/code maps statuses to project sections. Configure them in `.devintern-code/settings.json` using the project GID as the project key: ```json { "asana": { "projects": { "1200000000000000": { "inProgressStatus": "In Progress", "todoStatus": "To Do", "prStatus": "In Review" } } } } ``` Section names must match your project's sections (case-insensitive). Transitioning to `done`, `completed`, or `closed` marks the task complete instead of moving sections. ## Running a task Pass a task GID or a full task URL: ```bash # Task GID devintern 1200000000000001 --create-pr # Full task URL devintern https://app.asana.com/0/1200000000000000/1200000000000001 --create-pr ``` This workflow: 1. Fetches the task notes, tags, attachments, and comments 2. Runs a feasibility assessment (skippable with `--skip-clarity-check`) 3. Moves the task to the `inProgressStatus` section (unless `--skip-comments` is set) 4. Creates a feature branch, runs your agent, commits, and optionally opens a PR 5. Moves the task to the `prStatus` section after PR creation 6. Posts implementation or assessment comments on the task ## Batch processing with --query Asana has no query language, so @devintern/code provides a small filter syntax: ```bash devintern --query 'section:"To Do" completed:false' --create-pr devintern --query 'project:1200000000000000 assignee:Ada login bug' --create-pr ``` Supported filters: `project:` (defaults to `ASANA_DEFAULT_PROJECT_GID`), `section:""`, `assignee:`, `completed:true|false`. Any remaining text matches task names (case-insensitive). The integration lists the project's tasks (first 100) and filters them locally, which keeps it working on free Asana plans (the workspace search API requires Premium). ## Story points estimation Asana has no built-in estimation field. If your project has a numeric custom field for points, name it in `.devintern-code/.env`: ```bash ASANA_STORY_POINTS_FIELD=Story Points ``` With the field configured, `devintern --estimate` sets the custom field value and posts an estimation comment. Without it, estimates are posted as comments only. ## Limitations - **Batch size:** queries read the first 100 tasks of a project. Narrow with sections for larger projects. - **Rich text:** comments are posted as Asana rich text with a plain-text fallback when formatting is rejected. - **Comments:** use `--skip-comments` to skip task comments and section moves for a run. ## Troubleshooting **"Missing required Asana environment variables"** Ensure `ASANA_API_TOKEN` is set in `.devintern-code/.env`. **"Cannot resolve a project for task"** The task is not in any project and no `ASANA_DEFAULT_PROJECT_GID` is set. Add the task to a project or set the default project GID. **"Section \"In Progress\" not found in the project"** Check the section names in your project against `settings.json`. Available sections are listed in the error message. **"Asana task search requires a project"** Pass `project:` in the query or set `ASANA_DEFAULT_PROJECT_GID`. --- # Azure DevOps Integration @devintern/code can implement work directly from Azure DevOps work items: fetch work item details and comments, run a feasibility check, move the item through workflow states, execute your AI agent, commit changes, open a pull request, and post results back to the work item. ## Prerequisites - [Bun](https://bun.sh) and `@getdevintern/code` installed globally - Azure DevOps Personal Access Token (PAT) with Work Items read/write - Git repository for your project ## Setup ### 1. Set the task tracker In `.devintern-code/.env`: ```bash TASK_TRACKER=azure-devops ``` ### 2. Add Azure DevOps credentials ```bash AZURE_DEVOPS_ORG=your-organization AZURE_DEVOPS_PAT=your-pat-token AZURE_DEVOPS_PROJECT=YourProject ``` The organization is the segment after `dev.azure.com/` in your URLs. Create the PAT under User settings, Personal access tokens, with the **Work Items (Read and write)** scope. The project is the project name that contains your work items. ### 3. Configure state transitions Edit `.devintern-code/settings.json` using the project name as the project key: ```json { "azure-devops": { "projects": { "YourProject": { "inProgressStatus": "Active", "todoStatus": "New", "prStatus": "Resolved" } } } } ``` State names must match the workflow states of your work item types (for example `New`, `Active`, `Resolved`, `Closed` in the Agile template). @devintern/code moves work items to: - **inProgressStatus** when implementation starts (after the clarity check) - **prStatus** after a pull request is created - **todoStatus** when implementation is incomplete or max turns are reached See [Configuration](/docs/code/configuration) for all settings fields. ## Running a work item Pass a numeric work item ID or a full work item URL: ```bash # Work item ID devintern 4211 --create-pr # Full work item URL devintern https://dev.azure.com/your-org/YourProject/_workitems/edit/4211 --create-pr ``` This workflow: 1. Fetches the work item description (HTML converted to markdown), tags, attachments, and comments 2. Runs a feasibility assessment (skippable with `--skip-clarity-check`) 3. Moves the item to `inProgressStatus` (unless `--skip-comments` is set) 4. Creates a feature branch, runs your agent, commits, and optionally opens a PR 5. Moves the item to `prStatus` after PR creation 6. Posts implementation or assessment comments on the work item Parent, child, and related work item links are fetched and included in the agent context. ## Batch processing with --query Select multiple work items with [WIQL](https://learn.microsoft.com/en-us/azure/devops/boards/queries/wiql-syntax), scoped to your configured project: ```bash devintern --query "SELECT [System.Id] FROM WorkItems WHERE [System.State] = 'New'" --create-pr devintern --query "SELECT [System.Id] FROM WorkItems WHERE [System.Tags] CONTAINS 'bug'" --create-pr ``` The first 100 matching work items are processed in sequence. ## Story points estimation Estimation mode is fully supported via the `Microsoft.VSTS.Scheduling.StoryPoints` field (the Agile template default): ```bash devintern 4211 --estimate ``` If your process template uses a different field (for example `Effort` in Scrum), set `storyPointsField` in `settings.json` for the project. ## Limitations - **Description format:** work item descriptions are HTML. They are converted to markdown for the agent, and automation comments are converted from markdown to HTML before posting. - **Comments API:** Azure DevOps work item comments use a preview API version (7.1-preview.3), pinned by the integration. - **Comments:** use `--skip-comments` to skip work item comments and state transitions for a run. ## Troubleshooting **"Missing required Azure DevOps environment variables"** Ensure `AZURE_DEVOPS_ORG`, `AZURE_DEVOPS_PAT`, and `AZURE_DEVOPS_PROJECT` are set in `.devintern-code/.env`. **"Failed to move work item ... to state"** State names differ per process template and work item type. Check the states available for the work item type in your project settings and match them exactly in `settings.json`. **401 or 403 errors** Verify the PAT has not expired and includes the Work Items (Read and write) scope for the target organization. --- # GitHub Issues Integration @devintern/code can implement work directly from GitHub Issues: fetch issue details and comments, run a feasibility check, move status labels, execute your AI agent, commit changes, open a pull request in the same repository, and post results back on the issue. Looking for webhook-based PR review automation? See the [GitHub webhook server guide](/docs/code/github-integration/). ## Prerequisites - [Bun](https://bun.sh) and `@getdevintern/code` installed globally - GitHub personal access token - Git repository for your project ## Setup ### 1. Set the task tracker In `.devintern-code/.env`: ```bash TASK_TRACKER=github ``` ### 2. Add GitHub credentials ```bash GITHUB_TOKEN=ghp_xxxxxxxxxxxx GITHUB_REPO=owner/repo ``` The same `GITHUB_TOKEN` used for pull request creation works here. It needs the `repo` scope (classic token) or `Issues: Read and write` plus `Pull requests: Read and write` (fine-grained token). `GITHUB_REPO` is the repository whose issues you want to implement, in `owner/repo` form. ### 3. Configure status labels GitHub has no built-in workflow states, so @devintern/code maps statuses to labels. Create the labels in your repository, then configure them in `.devintern-code/settings.json` using `owner/repo` as the project key: ```json { "github": { "projects": { "acme/webapp": { "inProgressStatus": "In Progress", "todoStatus": "To Do", "prStatus": "In Review" } } } } ``` To keep statuses mutually exclusive, also list them in `.devintern-code/.env`: ```bash GITHUB_STATUS_LABELS=To Do,In Progress,In Review ``` When a status changes, @devintern/code adds the target label and removes the other labels in this list. Transitioning to `closed` or `done` closes the issue instead of applying a label. ## Running an issue Pass an issue number, `#number`, or a full issue URL: ```bash # Issue number devintern 123 --create-pr # Full issue URL devintern https://github.com/acme/webapp/issues/123 --create-pr ``` This workflow: 1. Fetches the issue body, labels, and comments 2. Runs a feasibility assessment (skippable with `--skip-clarity-check`) 3. Applies the `inProgressStatus` label (unless `--skip-comments` is set) 4. Creates a feature branch, runs your agent, commits, and optionally opens a PR 5. Applies the `prStatus` label after PR creation 6. Posts implementation or assessment comments on the issue ## Batch processing with --query Select multiple issues with [GitHub search qualifiers](https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests). The query is automatically scoped to your repository with `repo:owner/repo is:issue`: ```bash devintern --query "is:open label:bug" --create-pr devintern --query 'is:open "login flow"' --create-pr ``` The first 100 matching issues are processed in sequence. Note that GitHub's search API is rate-limited to 30 requests per minute. ## Story points estimation GitHub Issues has no estimation field, so `--estimate` runs in comment-only mode: the analysis is posted (or updated) as an issue comment with the suggested points, reasoning, risks, and unclear areas. ## Limitations - **Attachments:** GitHub has no attachment API. Images and files embedded in the issue body (`user-attachments` links) are downloaded for the agent. - **Status labels:** labels named in `settings.json` must already exist in the repository. The error message lists available labels when one is missing. - **Comments:** use `--skip-comments` to skip issue comments and label transitions for a run. ## Troubleshooting **"Missing required GitHub environment variables"** Ensure `GITHUB_TOKEN` and `GITHUB_REPO` are set in `.devintern-code/.env`. **"Label \"In Progress\" not found in the repository"** Create the label in your repository (Issues → Labels) or change the status names in `settings.json` to match existing labels. **Old status labels pile up on issues** Set `GITHUB_STATUS_LABELS` to the full list of status label names so transitions remove the previous status. **Search returns pull requests** Queries are scoped with `is:issue` automatically. If you pass your own `repo:` qualifier, include `is:issue` yourself. --- # Linear Integration @devintern/code can implement work directly from Linear issues: fetch issue details and comments, run a feasibility check, move the issue through your workflow states, execute your AI agent, commit changes, open a pull request, and post results back to the issue. ## Prerequisites - [Bun](https://bun.sh) and `@getdevintern/code` installed globally - Linear Personal API key - Git repository for your project ## Setup ### 1. Set the task tracker In `.devintern-code/.env`: ```bash TASK_TRACKER=linear ``` ### 2. Add your Linear API key ```bash LINEAR_API_KEY=lin_api_xxxxxxxxxxxx ``` Create the key at [Linear API settings](https://linear.app/settings/api): under **Personal API keys**, click **Create key**, add a label (e.g. `DevIntern`), and copy the key. It starts with `lin_api_` and cannot be viewed again after you leave the page. The key inherits your Linear account permissions. ### 3. Configure workflow state transitions Edit `.devintern-code/settings.json` using the team key (the prefix in identifiers like `ENG-42`) as the project key: ```json { "linear": { "projects": { "ENG": { "inProgressStatus": "In Progress", "todoStatus": "Backlog", "prStatus": "In Review" } } } } ``` State names must match your team's workflow states (case-insensitive). @devintern/code moves issues to: - **inProgressStatus** when implementation starts (after the clarity check) - **prStatus** after a pull request is created - **todoStatus** when implementation is incomplete or max turns are reached See [Configuration](/docs/code/configuration) for all settings fields. ## Running an issue Pass an issue identifier or a full issue URL: ```bash # Identifier devintern ENG-42 --create-pr # Full issue URL devintern https://linear.app/acme/issue/ENG-42/fix-login-bug --create-pr ``` This workflow: 1. Fetches the issue description, labels, attachments, and comments 2. Runs a feasibility assessment (skippable with `--skip-clarity-check`) 3. Moves the issue to `inProgressStatus` (unless `--skip-comments` is set) 4. Creates a feature branch, runs your agent, commits, and optionally opens a PR 5. Moves the issue to `prStatus` after PR creation 6. Posts implementation or assessment comments on the issue ## Batch processing with --query Select multiple issues with a [Linear IssueFilter](https://studio.linear.app/graphql) expressed as JSON: ```bash devintern --query '{"state":{"name":{"eq":"Todo"}},"team":{"key":{"eq":"ENG"}}}' --create-pr ``` Plain text works too and matches against issue titles (case-insensitive contains): ```bash devintern --query "login bug" --create-pr ``` The first 50 matching issues are processed in sequence. ## Story points estimation Linear has a native estimate field, so estimation mode is fully supported: ```bash devintern ENG-42 --estimate ``` This analyzes the issue, sets the native estimate value, and posts (or updates) an estimation comment with reasoning, risks, and unclear areas. ## Limitations - **Attachments:** files hosted on `uploads.linear.app` are downloaded with your API key. External attachment links (Figma, Google Docs) are passed to the agent as links. - **Comments:** use `--skip-comments` to skip Linear comments and state transitions for a run. ## Troubleshooting **"Missing required Linear environment variables"** Ensure `LINEAR_API_KEY` is set in `.devintern-code/.env`. **Issue does not move between states** Confirm the state names in `settings.json` match your team's workflow states, and that the project key is the team key from the issue identifier (`ENG` for `ENG-42`). **"Workflow state \"In Progress\" not found for team"** Check spelling against your team's workflow states in Linear settings. Available states are shown in the error message. **"Invalid Linear IssueFilter JSON"** Quote the JSON filter in single quotes in your shell and validate it against the IssueFilter schema in the [Linear GraphQL explorer](https://studio.linear.app/graphql). --- # Trello Integration @devintern/code can implement work directly from Trello cards: fetch card details and comments, run a feasibility check, move the card between lists, execute your AI agent, commit changes, open a pull request, and post results back to the card. ## Prerequisites - [Bun](https://bun.sh) and `@getdevintern/code` installed globally - Trello Power-Up API key and user token with read/write access - Git repository for your project ## Setup ### 1. Set the task tracker In `.devintern-code/.env`: ```bash TASK_TRACKER=trello ``` ### 2. Add Trello credentials Both variables are required for @devintern/code: ```bash TRELLO_API_KEY=your-power-up-api-key TRELLO_API_TOKEN=your-user-token ``` **API key:** Create or open a Power-Up at [trello.com/power-ups/admin](https://trello.com/power-ups/admin), then copy the API key from the **API Key** tab. **API token:** Generate a token by visiting (replace `YOUR_API_KEY` with your key): ``` https://trello.com/1/authorize?expiration=never&scope=read,write&response_type=token&name=DevIntern&key=YOUR_API_KEY ``` Click **Allow**, then copy the token from the page. Optionally pin a default board: ```bash TRELLO_DEFAULT_BOARD_ID=abc123 # board short ID from trello.com/b/{boardId}/board-name ``` `TRELLO_DEFAULT_BOARD_ID` is used for settings lookup when the board cannot be inferred from the card. Status transitions are driven entirely by the list names you configure in `settings.json` (see below), not by an environment variable. ### 3. Configure list transitions Edit `.devintern-code/settings.json` using the board short ID as the project key: ```json { "trello": { "projects": { "abc123": { "inProgressStatus": "Doing", "todoStatus": "To Do", "prStatus": "Review" } } } } ``` List names must match your board exactly (case-insensitive). @devintern/code moves cards to: - **inProgressStatus** when implementation starts (after the clarity check) - **prStatus** after a pull request is created - **todoStatus** when implementation is incomplete or max turns are reached See [Configuration](/docs/code/configuration) for all settings fields. ## Running a card Pass a card short link, full URL, or 24-character card ID: ```bash # Short link devintern 4uWKPOTv --create-pr # Full card URL devintern https://trello.com/c/4uWKPOTv/card-slug --create-pr ``` This workflow: 1. Fetches card description, labels, and comments 2. Runs a feasibility assessment (skippable with `--skip-clarity-check`) 3. Moves the card to `inProgressStatus` (unless `--skip-comments` is set) 4. Creates a feature branch, runs your agent, commits, and optionally opens a PR 5. Moves the card to `prStatus` after PR creation 6. Posts implementation or assessment comments on the card ## Batch processing with --query Select multiple cards with [Trello search operators](https://support.atlassian.com/trello/docs/searching-for-cards-all-boards/), scoped to `TRELLO_DEFAULT_BOARD_ID` when set: ```bash devintern --query 'list:"To Do" is:open' --create-pr devintern --query 'label:bug login' --create-pr ``` The first 100 matching cards are processed in sequence. ## Limitations - **Story points:** Estimation mode (`--estimate`) is not supported for Trello (no estimation concept). Estimation works with Jira, Linear, Azure DevOps, and Asana. - **Comments:** Use `--skip-comments` to skip Trello comments and list transitions for a run. ## Troubleshooting **"Missing required Trello environment variables"** Ensure `TRELLO_API_KEY` and `TRELLO_API_TOKEN` are set in `.devintern-code/.env`. **Card does not move between lists** Confirm list names in `settings.json` match your board, and that the project key is the board short ID (from the board URL, not the 24-character internal ID). Set `TRELLO_DEFAULT_BOARD_ID` to the same short ID if needed. **"List \"Doing\" not found on board"** Check spelling and capitalization against the lists on your Trello board. Available lists are shown in the error message. **Transitions skipped entirely** List moves are tied to comment posting. Do not pass `--skip-comments` if you expect cards to move. --- # Markdown File Tasks You can run `devintern` against a local markdown file instead of a Jira issue or Trello card. No PM credentials are required when every argument is a file path. This is useful for one-off tasks, local specs, or projects that do not use an external task tracker. ## Direct file path mode Pass one or more `.md` file paths (relative or absolute) directly as arguments: ```bash # Single file devintern ./tasks/feature-spec.md # With PR creation devintern ./tasks/feature-spec.md --create-pr # Multiple files (processed in sequence) devintern ./epic.md ./subtask-a.md ./subtask-b.md # Skip branch creation devintern ./tasks/feature-spec.md --no-git ``` Any argument ending in `.md` or containing a path separator (`/` or `./`) is treated as a file path. Jira and Trello keys that do not match are routed to the configured task tracker as normal, so you can mix both in one command: ```bash # PM task + local file in one run devintern PROJ-123 ./extra-context.md ``` When all arguments are file paths, `devintern` skips PM credential validation entirely so no `.devintern-code/.env` is needed for the task tracker section. ## Markdown file format The file is passed directly to the agent. Optionally add YAML frontmatter to control the task key and status tracking: ```markdown --- key: my-feature status: To Do type: Feature --- # Add user profile page Add a profile page at `/profile` that shows the signed-in user's name and avatar. ## Acceptance criteria - Route `/profile` renders user name and avatar - Redirects to `/login` when not authenticated ``` ### Frontmatter fields | Field | Required | Description | | ------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `key` | No | Task key used for the git branch name and output directory. Defaults to the filename stem (e.g. `feature-spec` for `feature-spec.md`). | | `status` | No | Current status. When present, `devintern` updates it to `In Progress` before running the agent and to `Done` on success. | | `type` | No | Issue type label (informational only; defaults to `Task`). | | `created_at` | No | Creation timestamp (informational only). | Frontmatter is optional. A file with no frontmatter works fine: the filename stem is used as the task key and no status tracking is applied. ### Title extraction The task title is taken from the first `# H1` heading in the file. If no H1 is present, the filename stem is used as the title. ### Implementation instructions If the file does not already contain an `## Implementation Instructions` section, `devintern` appends a short prompt at the end of the content passed to the agent. You can include your own instructions in the file to override this. ## Git branch name The branch is created as `feature/{key}` where `{key}` is: 1. The `key:` frontmatter value (if set), or 2. The filename stem (e.g. `my-task.md` produces `feature/my-task`) Special characters are replaced with hyphens for git compatibility. ## Status tracking When the frontmatter includes a `status:` field, `devintern` updates it in place: - Set to `In Progress` just before the agent runs - Set to `Done` after a successful run (detected by the presence of `implementation-summary.md`) - Left as `In Progress` if the run fails or is incomplete The file is edited in place, so commit it to version control if you want to track these transitions. ## TASK_TRACKER=markdown mode For projects that store all task files in a single directory, you can set the tracker to `markdown` and pass short task keys instead of file paths: ```bash # .devintern-code/.env TASK_TRACKER=markdown MARKDOWN_TASKS_DIR=/path/to/tasks ``` Then run: ```bash devintern my-feature --create-pr ``` `devintern` resolves `my-feature` to `{MARKDOWN_TASKS_DIR}/my-feature.md` (or uses the `key:` frontmatter field to find the file). All standard flags work the same way. `MARKDOWN_TASKS_DIR` is required when `TASK_TRACKER=markdown`. ### Batch processing with --query Select multiple task files by frontmatter fields, with optional free text matched against titles: ```bash devintern --query "status=todo" --create-pr devintern --query 'status="In Progress" type=bug login' --create-pr ``` Each space-separated `key=value` pair must match the file's frontmatter (values compared case-insensitively; quote values containing spaces). Files without frontmatter only match queries with no filters. ## What is and is not supported | Feature | File path mode | TASK_TRACKER=markdown | | ------------------------ | ---------------------------------- | ------------------------- | | No PM credentials needed | Yes | Yes | | Status field auto-update | Yes (if frontmatter has `status:`) | Yes | | Git branch creation | Yes | Yes | | `--create-pr` | Yes | Yes | | `--auto-review` | Yes | Yes | | `--skip-clarity-check` | Yes | Yes | | `--no-git` | Yes | Yes | | Batch `--query` | No | Yes (frontmatter filters) | | Post comments to tracker | No | No | | Story point estimation | No | No | | Attachments | No | No | ## Troubleshooting **"File not found"** Check that the path is correct and the file exists. Relative paths are resolved from the current working directory. **"File appears to be binary"** The file contains a null byte. Ensure it is a plain text `.md` file. **"Missing required markdown environment variable: MARKDOWN_TASKS_DIR"** Set `MARKDOWN_TASKS_DIR` in `.devintern-code/.env` when using `TASK_TRACKER=markdown`. **Status is not updated after the run** The frontmatter must contain a `status:` field. A file without a `status:` field is processed normally but the field is not written. **Branch name looks wrong** Check the `key:` field in the frontmatter. If no `key:` is set, the branch is derived from the filename. Rename the file or add a `key:` field to control it explicitly. --- # Server Automation # GitHub Integration Guide This guide covers secure deployment options for the @devintern/code webhook server to automatically address PR review comments. ## Table of Contents - [Webhook Server Deployment Guide](#webhook-server-deployment-guide) - [Table of Contents](#table-of-contents) - [Overview](#overview) - [Prerequisites](#prerequisites) - [Exposure Options](#exposure-options) - [Option 1: Cloudflare Tunnel (Recommended)](#option-1-cloudflare-tunnel-recommended) - [Option 2: Tailscale Funnel](#option-2-tailscale-funnel) - [Option 3: Reverse Proxy (Caddy/nginx)](#option-3-reverse-proxy-caddynginx) - [Caddy (Automatic HTTPS)](#caddy-automatic-https) - [nginx](#nginx) - [Option 4: Direct Exposure (Not Recommended)](#option-4-direct-exposure-not-recommended) - [Security Layers](#security-layers) - [1. Webhook Signature Verification (Critical)](#1-webhook-signature-verification-critical) - [2. GitHub IP Allowlisting (Recommended)](#2-github-ip-allowlisting-recommended) - [3. Rate Limiting](#3-rate-limiting) - [4. Firewall Rules (OS-level)](#4-firewall-rules-os-level) - [5. TLS/HTTPS (Required by GitHub)](#5-tlshttps-required-by-github) - [GitHub App Configuration](#github-app-configuration) - [Update App Permissions](#update-app-permissions) - [Configure Webhook](#configure-webhook) - [Running the Server](#running-the-server) - [Environment Variables](#environment-variables) - [Start the Server](#start-the-server) - [Systemd Service (Linux)](#systemd-service-linux) - [Monitoring & Troubleshooting](#monitoring--troubleshooting) - [Logs](#logs) - [Health Check](#health-check) - [Test Webhook Delivery](#test-webhook-delivery) - [Common Issues](#common-issues) - [Debug Mode](#debug-mode) - [Security Checklist](#security-checklist) - [Quick Start Summary](#quick-start-summary) ## Overview The webhook server listens for GitHub PR events and automatically runs Agent to address feedback. The architecture looks like: ``` GitHub → [Exposure Layer] → Webhook Server → Agent Harness → Git Push ``` **Key Security Principle**: The webhook server should never be directly exposed to the internet. Always use one of the secure exposure options below. ### What triggers a run The server acts on these events, and in every case it only proceeds when the bot is **`@`-mentioned** (so it never fires on unrelated comments): | You do this | GitHub event | Notes | | ---------------------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------- | | Submit a review with **Request changes** | `pull_request_review` (`changes_requested`) | Inline diff comments are addressed in one batch | | Submit a review with **Comment** | `pull_request_review` (`commented`) | Same batch flow; e.g. `@bot please tweak these` | | Leave a **top-level PR comment** | `issue_comment` | e.g. `@bot finish implementing this PR`, needs the **Issue comments** event subscribed | Inline review comments are processed _as a batch_ with their parent review. The standalone `pull_request_review_comment` event is intentionally ignored to avoid acting on each line comment separately. Approvals and dismissals are never actionable. ## Prerequisites 1. **GitHub App** configured with webhook permissions 2. **Webhook Secret** - a random string for request verification 3. **Agent Harness CLI** installed and configured 4. **Git credentials** with push access to target repositories 5. **Automation license**: the webhook server is unattended automation, so it requires an automation license (Supporter, Team, or Business) > **License required.** Like scheduled runs, `devintern serve` runs unattended and fails the license check without an automation license. Set `LICENSE_KEY` in your `.devintern-code/.env` (or as an `Environment=` entry in the service) to an automation license key (Supporter, Team, or Business) from [devintern.com/account](https://devintern.com/account). Generate a webhook secret: ```bash openssl rand -hex 32 ``` You generate this secret yourself. It is **not** issued by GitHub. Put the **same** value in two places: the GitHub App's webhook **Secret** field and `WEBHOOK_SECRET` in your environment. GitHub HMAC-signs each delivery with it, and the server verifies the signature. ## Exposure Options ### Option 1: Cloudflare Tunnel (Recommended) **Zero open ports** - Cloudflare Tunnel creates an outbound-only connection from your server to Cloudflare's edge network. **Pros:** - No inbound ports to open on your firewall - Free tier available - DDoS protection included - Automatic HTTPS - Works behind NAT/firewalls **Setup:** 1. Install cloudflared: ```bash # macOS brew install cloudflared # Linux curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb sudo dpkg -i cloudflared.deb # Or download from https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/installation/ ``` 2. Authenticate: ```bash cloudflared tunnel login ``` 3. Create a tunnel: ```bash cloudflared tunnel create devintern-webhooks ``` 4. Configure DNS (creates webhooks.yourdomain.com): ```bash cloudflared tunnel route dns devintern-webhooks webhooks.yourdomain.com ``` If a DNS record for that hostname already exists (e.g. left over from a previous tunnel), this fails with `code: 1003 … record with that host already exists`. Repoint it at the new tunnel with `--overwrite-dns`: ```bash cloudflared tunnel route dns --overwrite-dns devintern-webhooks webhooks.yourdomain.com ``` 5. Create config file (`~/.cloudflared/config.yml`): ```yaml tunnel: YOUR_TUNNEL_ID credentials-file: /path/to/.cloudflared/YOUR_TUNNEL_ID.json ingress: - hostname: webhooks.yourdomain.com service: http://localhost:3000 - service: http_status:404 ``` 6. Run the tunnel: ```bash # Foreground cloudflared tunnel run devintern-webhooks # Or as a service sudo cloudflared service install sudo systemctl start cloudflared ``` **Final architecture:** ``` GitHub → Cloudflare Edge → Cloudflare Tunnel → localhost:3000 ↓ Webhook Server (no open ports) ``` --- ### Option 2: Tailscale Funnel If you already use Tailscale for your network, Funnel provides a simple way to expose services. **Pros:** - Simple one-command setup - Integrates with existing Tailscale network - Automatic HTTPS with valid certificates **Setup:** 1. Enable Funnel in Tailscale admin console (requires admin access) 2. Start the funnel: ```bash tailscale funnel 3000 ``` 3. Your webhook URL will be: `https://your-machine-name.tailnet-name.ts.net` **Note:** Tailscale Funnel has some limitations on free plans. Check [Tailscale Funnel docs](https://tailscale.com/kb/1223/tailscale-funnel/). --- ### Option 3: Reverse Proxy (Caddy/nginx) Use when you have a server with a public IP and want full control. #### Caddy (Automatic HTTPS) 1. Install Caddy: ```bash # Debian/Ubuntu sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list sudo apt update && sudo apt install caddy # macOS brew install caddy ``` 2. Configure (`/etc/caddy/Caddyfile`): ```caddyfile webhooks.yourdomain.com { reverse_proxy localhost:3000 # Rate limiting rate_limit { zone webhooks { key {remote_host} events 30 window 1m } } # Optional: IP allowlisting for GitHub # See "GitHub IP Ranges" section below for current IPs @blocked not remote_ip 140.82.112.0/20 143.55.64.0/20 185.199.108.0/22 192.30.252.0/22 respond @blocked 403 } ``` 3. Start Caddy: ```bash sudo systemctl enable caddy sudo systemctl start caddy ``` #### nginx 1. Configure (`/etc/nginx/sites-available/webhooks`): ```nginx server { listen 443 ssl http2; server_name webhooks.yourdomain.com; ssl_certificate /etc/letsencrypt/live/webhooks.yourdomain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/webhooks.yourdomain.com/privkey.pem; # Rate limiting limit_req_zone $binary_remote_addr zone=webhooks:10m rate=30r/m; location / { limit_req zone=webhooks burst=5; # Optional: GitHub IP allowlisting # allow 140.82.112.0/20; # allow 143.55.64.0/20; # allow 185.199.108.0/22; # allow 192.30.252.0/22; # deny all; proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` 2. Get SSL certificate: ```bash sudo certbot certonly --nginx -d webhooks.yourdomain.com ``` 3. Enable and start: ```bash sudo ln -s /etc/nginx/sites-available/webhooks /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx ``` --- ### Option 4: Direct Exposure (Not Recommended) **⚠️ Only use this for testing with tools like ngrok** ```bash # ngrok (temporary testing only) ngrok http 3000 ``` Never expose the webhook server directly to the internet in production. --- ## Security Layers Regardless of which exposure method you choose, **always implement these security measures**: ### 1. Webhook Signature Verification (Critical) This is implemented in devintern and **cannot be bypassed**. GitHub signs every webhook with your secret: ```bash # Set your webhook secret export WEBHOOK_SECRET="your-random-secret-here" ``` The server will reject any request without a valid `X-Hub-Signature-256` header. ### 2. GitHub IP Allowlisting (Recommended) GitHub publishes their webhook IP ranges at `https://api.github.com/meta`. Current ranges (may change): ``` 140.82.112.0/20 143.55.64.0/20 185.199.108.0/22 192.30.252.0/22 ``` **Dynamically fetch current IPs:** ```bash curl -s https://api.github.com/meta | jq '.hooks' ``` ### 3. Rate Limiting Prevent abuse even from valid sources. Recommended limits: - 30 requests per minute per IP - Burst of 5 requests ### 4. Firewall Rules (OS-level) If using direct exposure or reverse proxy, add firewall rules: ```bash # UFW (Ubuntu) sudo ufw allow from 140.82.112.0/20 to any port 3000 sudo ufw allow from 143.55.64.0/20 to any port 3000 sudo ufw allow from 185.199.108.0/22 to any port 3000 sudo ufw allow from 192.30.252.0/22 to any port 3000 # iptables iptables -A INPUT -p tcp --dport 3000 -s 140.82.112.0/20 -j ACCEPT iptables -A INPUT -p tcp --dport 3000 -s 143.55.64.0/20 -j ACCEPT iptables -A INPUT -p tcp --dport 3000 -j DROP ``` ### 5. TLS/HTTPS (Required by GitHub) GitHub requires HTTPS for production webhooks. All exposure options above provide automatic HTTPS except direct exposure. --- ## GitHub App Configuration ### Update App Permissions Add these permissions to your GitHub App: 1. Go to your GitHub App settings 2. Navigate to **Permissions & events** 3. Under **Repository permissions**, add: - **Pull request review comments**: Read and write - **Issue comments**: Read and write (for top-level PR comments) 4. Under **Subscribe to events**, enable: - Pull request review - Pull request review comment - Issue comment (lets a user trigger devintern with a top-level PR comment) ### Configure Webhook 1. In your GitHub App settings, go to **Webhooks** 2. Set **Webhook URL** to your server's URL (e.g., `https://webhooks.yourdomain.com/webhooks/github`) 3. Set **Secret** to your `WEBHOOK_SECRET` 4. Select content type: `application/json` 5. Enable events: - Pull request reviews - Pull request review comments --- ## Running the Server ### Environment Variables ```bash # Required export WEBHOOK_SECRET="your-webhook-secret" export LICENSE_KEY="your-automation-license-key" # GitHub authentication. # GitHub App auth is preferred: it resolves the bot identity (slug[bot]), # which is REQUIRED for @mention matching and bot-attributed commits: export GITHUB_APP_ID="123456" export GITHUB_APP_PRIVATE_KEY_PATH="/path/to/key.pem" # OR export GITHUB_APP_PRIVATE_KEY_BASE64="..." # A personal access token works as a fallback when no App is configured: export GITHUB_TOKEN="ghp_..." # Optional export WEBHOOK_PORT="3000" # Default: 3000 export WEBHOOK_HOST="0.0.0.0" # Default: 0.0.0.0 export WEBHOOK_AUTO_REPLY="true" # Reply to addressed comments export WEBHOOK_AUTO_REVIEW="true" # Run self-review loop after addressing feedback export WEBHOOK_AUTO_REVIEW_MAX_ITERATIONS="5" # Max review iterations (default: 5) export WEBHOOK_MAX_RETRIES="3" # Retries per failed webhook job export WEBHOOK_VALIDATE_IP="true" # Reject requests outside GitHub's published IP ranges export WEBHOOK_QUEUE_DB="/tmp/devintern-webhooks/queue.db" # Persistent job queue path export WEBHOOK_DEBUG="true" # Verbose request/processing logging ``` > **Auth precedence (serve mode):** unlike the CLI (where `GITHUB_TOKEN` takes precedence), the webhook server is **App-first**. When GitHub App credentials are present they are used even if a `GITHUB_TOKEN` is also set, because the App resolves the bot identity needed for `@mention` matching and bot-attributed commits. A token alone still works (the App falls back to it when no App is configured), but mention-gated triggers and `slug[bot]` commit attribution require the App. ### Start the Server ```bash # Development bun run src/webhook-server.ts # Production (after build) devintern serve --port 3000 # With PM2 (process manager) pm2 start "devintern serve" --name devintern-webhooks ``` ### Systemd Service (Linux) Create `/etc/systemd/system/devintern-webhooks.service`: ```ini [Unit] Description=@devintern/code Webhook Server After=network.target [Service] Type=simple User=your-user WorkingDirectory=/path/to/your/projects # devintern is a `#!/usr/bin/env bun` script: pin PATH so `bun` resolves # under systemd's minimal environment (check: dirname "$(which bun)"). Environment=PATH=/home/your-user/.local/bin:/home/your-user/.local/share/mise/installs/bun/1.3.2/bin:/usr/local/bin:/usr/bin Environment=LICENSE_KEY=your-automation-license-key Environment=WEBHOOK_SECRET=your-secret Environment=GITHUB_TOKEN=ghp_... Environment=WEBHOOK_AUTO_REPLY=true Environment=WEBHOOK_AUTO_REVIEW=true ExecStart=/usr/local/bin/devintern serve --port 3000 Restart=always RestartSec=10 [Install] WantedBy=multi-user.target ``` Enable and start: ```bash sudo systemctl daemon-reload sudo systemctl enable devintern-webhooks sudo systemctl start devintern-webhooks ``` Rather than putting secrets in the unit file, you can load them from your project's `.env` with `EnvironmentFile=/path/to/.devintern-code/.env` and drop the individual `Environment=` secret lines. **User service (no root):** to run under `systemctl --user` instead, place the unit in `~/.config/systemd/user/`, remove the `User=` line, set `WantedBy=default.target`, manage it with `systemctl --user enable --now devintern-webhooks`, and run `loginctl enable-linger "$USER"` so it survives logout. Pair it with a user-level `cloudflared` unit the same way. **Process cleanup.** While addressing a review, the agent may start long-running processes (dev servers, watchers) to verify its changes. Because the webhook server is always on, those would otherwise accumulate over its lifetime. @devintern/code runs each agent in its own process group and tears that group down as soon as the task finishes, so nothing is left running between reviews. On Linux, the unit's cgroup (default `KillMode=control-group`) also reaps everything when the service stops or restarts, including processes that fully daemonize. --- ## Monitoring & Troubleshooting ### Logs ```bash # Systemd journalctl -u devintern-webhooks -f # PM2 pm2 logs devintern-webhooks ``` ### Health Check The server exposes a health endpoint: ```bash curl https://webhooks.yourdomain.com/health # {"status": "ok", "timestamp": "..."} ``` ### Test Webhook Delivery 1. Go to your GitHub App settings → **Advanced** 2. View **Recent Deliveries** 3. Check response codes and bodies 4. Use **Redeliver** to test ### Common Issues | Issue | Solution | | -------------------------- | ------------------------------------------------------------------------------------------------------------------ | | 401 Unauthorized | Check `WEBHOOK_SECRET` matches GitHub App config | | 403 Forbidden | Check IP allowlisting if enabled | | 500 Internal Error | Check server logs for stack trace | | Timeout | Ensure Agent Harness CLI is installed and working | | No webhook received | Check GitHub App webhook URL and events | | 530 from the public URL | Tunnel up but DNS not routing: the CNAME is missing or points at an old tunnel; re-run `route dns --overwrite-dns` | | `Tunnel not found` in logs | Tunnel was deleted or credentials belong to another account; recreate with `cloudflared tunnel create` | | License check failed | Set `LICENSE_KEY` to an automation license key (Supporter, Team, or Business) | ### Debug Mode Run with verbose logging: ```bash WEBHOOK_DEBUG=true devintern serve ``` --- ## Security Checklist Before going to production: - [ ] Webhook secret is set and matches GitHub App - [ ] HTTPS is enabled (automatic with recommended options) - [ ] Rate limiting is configured - [ ] IP allowlisting is enabled (optional but recommended) - [ ] Server runs as non-root user - [ ] Logs are being collected - [ ] Health monitoring is set up - [ ] Firewall rules are configured (if applicable) --- ## Quick Start Summary **Fastest secure setup (Cloudflare Tunnel):** ```bash # 1. Install cloudflared brew install cloudflared # or appropriate package manager # 2. Create tunnel cloudflared tunnel login cloudflared tunnel create devintern-webhooks cloudflared tunnel route dns devintern-webhooks webhooks.yourdomain.com # (add --overwrite-dns if a record for that host already exists) # 3. Set environment export WEBHOOK_SECRET=$(openssl rand -hex 32) export LICENSE_KEY="your-automation-license-key" export GITHUB_TOKEN="ghp_..." # or GITHUB_APP_ID + GITHUB_APP_PRIVATE_KEY_PATH # 4. Start server devintern serve & # 5. Start tunnel cloudflared tunnel run devintern-webhooks # 6. Put the SAME WEBHOOK_SECRET in the GitHub App, and set its webhook URL to: # https://webhooks.yourdomain.com/webhooks/github ``` Your webhook server is now securely exposed with zero open ports! --- # Automated Task Processing Run @devintern/code on a schedule, without manual intervention. On modern Linux servers the recommended approach is **systemd timers**: you get structured logs via `journalctl`, restart-on-failure semantics, and no root crontab access required. **Cron** still works on any Unix-like system and is shown second as a fallback (macOS, BSD, Alpine, containers without an init system). ## Requires an automation license Unattended execution (systemd, cron, or any CI environment) requires an **automation license** (Supporter, Team, or Business). When @devintern/code detects an automated context but finds no matching license, the run fails immediately with: ``` ❌ License check failed Automated execution detected (CI / systemd / cron) but no automation license was found. ``` Set `LICENSE_KEY` in your project's `.devintern-code/.env` (or as an `Environment=` entry in the unit file) to an automation license key (Supporter, Team, or Business) from [devintern.com/account](https://devintern.com/account). Interactive runs (`devintern PROJ-123` from your terminal) are unaffected and do **not** require a license. @devintern/code is free to use interactively under the FSL license. ## systemd timers A systemd job is a pair of unit files: a one-shot `.service` that runs `devintern`, and a `.timer` that triggers it on a schedule. ### Every 10 minutes: process Intern-labeled tasks `/etc/systemd/system/devintern-intern.service`: ```ini [Unit] Description=Process Intern-labeled tasks with @devintern/code After=network-online.target Wants=network-online.target [Service] Type=oneshot User=devintern WorkingDirectory=/path/to/your/project ExecStart=/usr/local/bin/devintern \ --query 'statusCategory = "To Do" AND sprint in openSprints() AND labels IN (Intern) ORDER BY created DESC' \ --max-turns 500 \ --create-pr \ --pr-target-branch master StandardOutput=journal StandardError=journal ``` `/etc/systemd/system/devintern-intern.timer`: ```ini [Unit] Description=Run @devintern/code every 10 minutes [Timer] OnBootSec=2min OnUnitActiveSec=10min Persistent=true [Install] WantedBy=timers.target ``` Enable, start, and inspect: ```bash sudo systemctl daemon-reload sudo systemctl enable --now devintern-intern.timer # Show next scheduled run systemctl list-timers devintern-intern.timer # Tail recent runs journalctl -u devintern-intern.service -f ``` ### Hourly: process AutoImpl-labeled tasks Same `.service` shape, swap the `ExecStart` JQL: ```ini ExecStart=/usr/local/bin/devintern \ --query 'status = "To Do" AND labels IN (AutoImpl)' \ --create-pr ``` And use an hourly timer: ```ini [Timer] OnBootSec=5min OnUnitActiveSec=1h Persistent=true [Install] WantedBy=timers.target ``` ### Twice daily: high-priority bug sweep For wall-clock schedules, use `OnCalendar` instead of `OnUnitActiveSec`. This timer fires at 09:00 and 17:00 every day: ```ini [Timer] OnCalendar=*-*-* 09,17:00:00 Persistent=true [Install] WantedBy=timers.target ``` Matching `.service`: ```ini ExecStart=/usr/local/bin/devintern \ --query 'type = Bug AND priority = High AND status = "To Do" AND labels IN (Intern)' \ --max-turns 300 \ --create-pr ``` ## Cron If you're on a system without systemd, the same three jobs work as crontab entries: ```bash # Process tasks labeled "Intern" in open sprints every 10 minutes */10 * * * * cd /path/to/your/project && devintern --query 'statusCategory = "To Do" AND sprint in openSprints() AND labels IN (Intern) ORDER BY created DESC' --max-turns 500 --create-pr --pr-target-branch master >> /tmp/devintern-cron.log 2>&1 # Process AutoImpl-labeled tasks every hour 0 * * * * cd /path/to/your/project && devintern --query 'status = "To Do" AND labels IN (AutoImpl)' --create-pr >> /tmp/devintern-cron.log 2>&1 # Process high-priority bugs twice daily 0 9,17 * * * cd /path/to/your/project && devintern --query 'type = Bug AND priority = High AND status = "To Do" AND labels IN (Intern)' --max-turns 300 --create-pr >> /tmp/devintern-cron.log 2>&1 ``` ## Pin `PATH` so the `bun` shebang resolves The `devintern` binary is a `#!/usr/bin/env bun` script, so it needs **`bun` on `PATH`** to run. systemd services start with a minimal `PATH` that usually excludes wherever your version manager (mise, asdf, nvm) installed Bun. The unit then fails with `bun: command not found` (or silently can't find `devintern` itself). Pin `PATH` explicitly in the `[Service]` section, listing the directory that contains `bun` (and `devintern`): ```ini [Service] Environment="PATH=/home/youruser/.local/bin:/home/youruser/.local/share/mise/installs/bun/1.3.2/bin:/usr/local/bin:/usr/bin" ``` Confirm the path with `dirname "$(which bun)"`. The same applies to cron, which also runs with a stripped `PATH`: either set `PATH=` at the top of the crontab or call `devintern` by absolute path. ## Running as a user service (no root) The examples above install to `/etc/systemd/system` (system-wide, needs `sudo`). You can instead run entirely as your own user with **`systemctl --user`**: no root, and the unit can read your `~/.ssh` and version-manager installs directly. Place the unit in `~/.config/systemd/user/`, drop the `User=` line, and manage it with `systemctl --user enable --now .timer`. To keep user services running after you log out, enable lingering once: ```bash loginctl enable-linger "$USER" ``` ## Git push under automation If your repo's remote is SSH (`git@github.com:...`), the unattended run needs the SSH key reachable without an interactive agent. The cleanest approach is a `~/.ssh/config` host entry pointing the host at the right key: plain `git push` then resolves it (no `GIT_SSH_COMMAND` needed): ``` Host github.com IdentityFile ~/.ssh/your_key ``` A `--user` service inherits your `$HOME` and reads this automatically; a system service with `User=` reads that user's `~/.ssh`. Alternatively, use an HTTPS remote with a `GITHUB_TOKEN`. ## Cleaning up processes the agent leaves running While working a task, the AI agent often starts long-running processes to verify its changes: dev servers (`npm run dev`, `vite`), watchers, `docker compose up`, and so on. If the agent does not stop them, they would otherwise outlive the run and pile up across every scheduled execution. @devintern/code prevents this. Each agent is launched in its own process group, and the entire group (the agent plus anything it spawned) is torn down when the run ends, times out, or is interrupted. This works the same under systemd, cron, and macOS, so you do not need to do anything to enable it. Two layers back this up: 1. **In-process reaping (all platforms).** @devintern/code signals the whole process group on completion, on timeout, and on `SIGINT` / `SIGTERM` / `SIGHUP`. This is what protects cron jobs and macOS, which have no init-level cleanup of their own. 2. **systemd cgroup cleanup (Linux, bonus).** A systemd service confines all of its processes to a unit cgroup, and the default `KillMode=control-group` reaps that entire cgroup when the unit deactivates. This catches even processes that fully daemonize (call `setsid` themselves) and so escape the process group. No extra configuration is required: any process the agent leaves behind is cleaned up when the oneshot service exits. If you run @devintern/code under cron on Linux and want the same daemon-proof guarantee systemd gives, wrap the command in a transient scope so its children share one cgroup: ```bash */10 * * * * cd /path/to/your/project && systemd-run --user --scope --collect devintern --query '...' --create-pr >> /tmp/devintern-cron.log 2>&1 ``` ## Linear schedules For Linear (`TASK_TRACKER=linear`), swap JQL for a JSON `IssueFilter`. Wrap the JSON in single quotes so the shell passes it through unchanged: ```ini # systemd service: process "intern"-labeled issues every 10 minutes ExecStart=/usr/local/bin/devintern \ --query '{"labels":{"name":{"eq":"intern"}}}' \ --max-turns 500 \ --create-pr \ --pr-target-branch master ``` ```bash # cron: process "intern"-labeled Linear issues every 10 minutes */10 * * * * cd /path/to/your/project && devintern --query '{"labels":{"name":{"eq":"intern"}}}' --max-turns 500 --create-pr --pr-target-branch master >> /tmp/devintern-cron.log 2>&1 # cron: process high-priority issues assigned to me every hour 0 * * * * cd /path/to/your/project && devintern --query '{"assignee":{"isMe":{"eq":true}},"priority":{"lte":2}}' --create-pr >> /tmp/devintern-cron.log 2>&1 ``` ## Important Notes - Set `WorkingDirectory` (systemd) or `cd` (cron) to your project directory so the correct `.devintern-code/.env` is loaded - Use absolute paths to the `devintern` and agent binaries, or pin `PATH` explicitly in the unit file (see above) - Set `LICENSE_KEY` to an automation license key (Supporter, Team, or Business): unattended runs fail the license check without it - `--query` is the preferred flag; `--jql` still works but emits a deprecation warning - For systemd, `journalctl -u ` gives you logs; for cron, redirect stdout/stderr to a log file - For Jira, use `ORDER BY created DESC` to process newest tasks first - Test your query manually before scheduling (JQL in Jira's issue search; JSON `IssueFilter` in Linear's API explorer) --- # Story Points Estimation Use the `--estimate` flag to have your AI agent analyze JIRA tasks and automatically assign story point estimates. This is useful for backlog grooming, sprint planning, or keeping estimates up to date as tasks evolve. ## What It Does - Analyzes task description, comments, linked resources, and related issues - Produces a **Fibonacci-scale** estimate (1, 2, 3, 5, 8, 13, 21) - Provides **confidence level** (high / medium / low), reasoning, risks, and unclear areas - Sets the story points field directly in JIRA - Posts a rich estimation comment with full context ## Usage ### Single Task ```bash devintern PROJ-123 --estimate ``` ### Batch via JQL ```bash # Estimate all tasks in the backlog devintern --estimate --query "project = PROJ AND status = 'To Do'" # Estimate unestimated tasks in the current sprint devintern --estimate --query "project = PROJ AND sprint in openSprints() AND 'Story Points' is EMPTY" # Estimate recently updated tasks devintern --estimate --query "project = PROJ AND updated >= -7d" ``` `--jql` still works as a deprecated alias of `--query`. ## Smart Behavior ### Skip Recently Created Tasks Tasks created less than **24 hours ago** are automatically skipped. This gives the team time to refine the description before estimation. ### Smart Re-Estimation If a task already has an estimation comment: - If the task hasn't been updated since the last estimate → **skipped** - If the task was updated after the last estimate → **re-estimated in place** (existing comment is updated, not duplicated) This keeps estimates fresh without creating comment clutter. ## Story Points Scale | Points | Meaning | | ------ | -------------------------------------------------------- | | **1** | Trivial change, config tweak, typo fix | | **2** | Small, well-defined task, single file change | | **3** | Moderate task, a few files, clear requirements | | **5** | Significant feature, multiple files, some complexity | | **8** | Large feature, cross-cutting concerns, integration work | | **13** | Very large, multiple subsystems, high complexity | | **21** | Epic-sized, major architectural change, high uncertainty | ## Configuration ### Story Points Field The tool auto-discovers the story points field in JIRA by searching for common names like: - `Story Points` - `Story Point Estimate` - `Story point estimate` If your JIRA instance uses a custom field name, you can override it in `.devintern-code/settings.json`: ```json { "projects": { "PROJ": { "storyPointsField": "customfield_10016" } } } ``` ## Automated Estimation Run estimation on a schedule via systemd timers (recommended on modern Linux servers) or cron (any Unix-like system without systemd). ### systemd timers A systemd job is a one-shot `.service` plus a `.timer` that triggers it. Below is the daily morning run; the weekly grooming and sprint-planning helpers follow the same shape with different `OnCalendar` expressions and JQL. #### Daily estimation (weekday mornings) Run every weekday at 9 AM to estimate new tasks. `/etc/systemd/system/devintern-estimate-daily.service`: ```ini [Unit] Description=Daily estimation of NeedsEstimate tasks After=network-online.target Wants=network-online.target [Service] Type=oneshot User=devintern WorkingDirectory=/path/to/your/project ExecStart=/usr/local/bin/devintern --estimate \ --query 'project = PROJ AND status = "To Do" AND labels IN (NeedsEstimate)' StandardOutput=journal StandardError=journal ``` `/etc/systemd/system/devintern-estimate-daily.timer`: ```ini [Unit] Description=Run daily estimation at 9 AM on weekdays [Timer] OnCalendar=Mon..Fri *-*-* 09:00:00 Persistent=true [Install] WantedBy=timers.target ``` Enable and inspect: ```bash sudo systemctl daemon-reload sudo systemctl enable --now devintern-estimate-daily.timer systemctl list-timers devintern-estimate-daily.timer journalctl -u devintern-estimate-daily.service -f ``` #### Weekly backlog grooming `OnCalendar` for Monday at 8 AM, JQL covers the last 7 days: ```ini # devintern-estimate-weekly.timer [Timer] OnCalendar=Mon *-*-* 08:00:00 Persistent=true [Install] WantedBy=timers.target ``` ```ini # devintern-estimate-weekly.service, ExecStart line ExecStart=/usr/local/bin/devintern --estimate \ --query 'project = PROJ AND status = "Backlog" AND updated >= -7d' ``` #### Sprint planning helper Wednesday at 10 AM, fills in any remaining gaps before planning: ```ini # devintern-estimate-sprint.timer [Timer] OnCalendar=Wed *-*-* 10:00:00 Persistent=true [Install] WantedBy=timers.target ``` ```ini # devintern-estimate-sprint.service, ExecStart line ExecStart=/usr/local/bin/devintern --estimate \ --query 'project = PROJ AND sprint in openSprints() AND "Story Points" is EMPTY' ``` ### Cron If you're not on systemd, the same three schedules work as crontab entries: ```bash # In crontab (run `crontab -e`) # Daily at 9 AM, weekdays: estimate new tasks 0 9 * * 1-5 cd /path/to/your/project && devintern --estimate --query 'project = PROJ AND status = "To Do" AND labels IN (NeedsEstimate)' >> /tmp/devintern-estimate.log 2>&1 # Weekly on Monday at 8 AM: re-estimate recently updated backlog 0 8 * * 1 cd /path/to/your/project && devintern --estimate --query 'project = PROJ AND status = "Backlog" AND updated >= -7d' >> /tmp/devintern-estimate.log 2>&1 # Weekly on Wednesday at 10 AM: sprint planning helper 0 10 * * 3 cd /path/to/your/project && devintern --estimate --query 'project = PROJ AND sprint in openSprints() AND "Story Points" is EMPTY' >> /tmp/devintern-estimate.log 2>&1 ``` ### Important Notes - Set `WorkingDirectory` (systemd) or `cd` (cron) to your project directory so the correct `.devintern-code/.env` is loaded - Use absolute paths to `devintern` and your agent binary, or pin `PATH` explicitly in the unit file - For systemd, `journalctl -u ` gives you logs; for cron, redirect stdout/stderr to a log file - Test your JQL query manually before scheduling - Tasks created less than 24 hours ago are automatically skipped, so frequent runs are safe ## Example Output ``` 📊 Running in estimation mode... ============================================================ 📊 Estimating: PROJ-456 🔄 Re-estimating PROJ-456: task updated since last estimate ✅ Estimated PROJ-456: 5 story points (high confidence) ============================================================ 📊 Estimation Summary: Estimated: 3 Skipped (< 24h old): 1 Skipped (not updated): 2 Failed: 0 ``` ## Troubleshooting **"Story points field not found"** - Check your JIRA instance's custom field name for story points - Set `storyPointsField` in `.devintern-code/settings.json` **"Failed to parse estimation response"** - The AI agent may have returned non-JSON output - Try running with `--verbose` to see the raw response - Check that your agent CLI is working correctly **Low confidence estimates** - The estimation comment will flag low confidence and ask for more details - Consider refining the task description before re-estimating --- # PM # Quick Start **@devintern/pm** automates story and task creation across multiple project management tools with AI. Transform Figma designs, error logs, or requirements into well-structured issues in seconds. ## Prerequisites - **[Bun](https://bun.sh) runtime**: Required to run @devintern/pm (the tool is built with Bun) - AI agent CLI installed and configured (e.g., Claude Code, OpenCode, Codex, Cursor) - Account with at least one supported PM tool (Jira, Linear, Trello, Azure DevOps, Asana, or GitHub) - **For Figma functionality**: [Figma MCP server](https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/) must be installed and configured in your AI agent (Claude Code only) ## Installation Install globally with Bun: ```bash bun install -g @getdevintern/pm ``` ## Initialize Configuration Navigate to your project directory and run: ```bash devpm init ``` In a terminal, this starts an interactive setup wizard that: - Detects an existing @devintern/code configuration (`.devintern-code/.env`) in the same project and offers to reuse those tracker credentials, so you skip straight to validation - Asks which tracker you use (Jira, Linear, Trello, Azure DevOps, Asana, GitHub Issues, or markdown files) - Links you directly to the provider's token creation page and prompts for each credential, with a pointer to the matching setup guide in these docs - Validates the connection with a real API call before finishing (you can retry, edit values, or skip) - Writes your answers to `.devintern-pm/.env` and updates your `.gitignore` to exclude `.devintern-pm/.env` (to prevent leaking secrets) For scripted or CI setups, pass `--yes` (or `--no-interactive`) to skip the prompts and write the configuration template instead. The non-interactive path also migrates matching values from `.devintern-code/.env` if present: ```bash devpm init --yes ``` ## First Run The interactive mode provides a step-by-step terminal UI for creating tasks. This is the recommended way to use @devintern/pm: ```bash devpm --interactive ``` The interactive mode will guide you through: 1. **Source type selection**: Choose between Figma URL, error log, or free-form prompt 2. **Source input**: Enter your Figma URL, error log, or requirements 3. **Custom instructions** (optional): Add additional requirements or focus areas 4. **Epic linking** (optional): Link to an existing Jira epic 5. **Issue type** (Jira, Azure DevOps, GitHub, Markdown only): Select Task, Story, Bug, Epic, or enter a custom type. Task is the default; press Enter to accept it. This step is skipped for Linear, Trello, and Asana, which do not support setting an issue type. 6. **Prompt style**: Choose between PM style or Technical style 7. **Confirmation**: Review your configuration before proceeding ## What's Next? - [Configure your PM backend](/docs/pm/configuration) - [Learn CLI usage patterns](/docs/pm/usage) --- # Configuration @devintern/pm uses per-project configuration stored in `.devintern-pm/.env` in your project directory. Run `devpm init` in a terminal for a guided setup: it asks which tracker you use, links to each provider's token creation page, validates the connection, and writes the file for you. Prefer editing by hand (or setting up in CI)? Run `devpm init --yes` to write the configuration template instead, then fill in values for your selected backend as described below. ## Select a Backend Set `TASK_TRACKER` to choose your PM tool. Defaults to `jira` if not specified. Supported backends: `jira`, `linear`, `trello`, `azure-devops`, `asana`, `github`, `markdown` ```bash TASK_TRACKER=jira ``` ## Backend-Specific Configuration Only configure the section that matches your `TASK_TRACKER`. Other backend variables are ignored. ### Jira ```bash TASK_TRACKER=jira JIRA_BASE_URL=https://your-org.atlassian.net JIRA_EMAIL=your-email@example.com JIRA_API_TOKEN=your-api-token JIRA_DEFAULT_PROJECT_KEY=PROJ ``` Create an API token at [https://id.atlassian.com/manage-profile/security/api-tokens](https://id.atlassian.com/manage-profile/security/api-tokens). Use the Atlassian account email that owns the token for `JIRA_EMAIL`. **Project key**: the short prefix on issue keys (e.g. `PROJ` in `PROJ-123`). Find it in any issue URL or under **Project settings → Details → Key**. See the [Jira Integration guide](/docs/pm/jira-integration) for step-by-step setup and troubleshooting. ### Linear ```bash TASK_TRACKER=linear LINEAR_API_KEY=lin_api_xxxxxxxxxxxx # LINEAR_DEFAULT_TEAM_KEY=ENG # optional, first team if omitted ``` Create a **Personal API key** at [https://linear.app/settings/api](https://linear.app/settings/api) (Settings → API → Personal API keys). Keys start with `lin_api_` and cannot be viewed again after creation. **Team key**: the short prefix on issue identifiers (e.g. `ENG` in `ENG-42`). Find it under team Settings → Key, or pick a team in interactive mode (Ctrl+P). See the [Linear Integration guide](/docs/pm/linear-integration) for step-by-step setup and troubleshooting. ### Trello `TRELLO_API_TOKEN` is the only required variable: @devintern/pm includes a bundled Power-Up key so you don't need to register your own app. ```bash TASK_TRACKER=trello TRELLO_API_TOKEN=your-api-token # required # TRELLO_API_KEY=your-api-key # optional: use your own Power-Up # TRELLO_DEFAULT_BOARD_ID=abc123 # optional, first board if omitted # TRELLO_DEFAULT_LIST_NAME="To Do" # optional, first list if omitted ``` See the [Trello Integration guide](/docs/pm/trello-integration) for step-by-step setup. ### Azure DevOps ```bash TASK_TRACKER=azure-devops AZURE_DEVOPS_ORG=your-organization AZURE_DEVOPS_PAT=your-personal-access-token AZURE_DEVOPS_PROJECT=YourProject ``` All three variables are **required**. Use the organization slug from your URL (`https://dev.azure.com/your-org/...` → `your-org`), not the full URL. Create a **Personal Access Token** at `https://dev.azure.com/your-org/_usersSettings/tokens` with **Work Items (Read & write)** and **Project and Team (Read)** scopes. **Project name**: must match exactly as shown in Azure DevOps (from the URL path or project picker). Work item types depend on your process template (Agile, Scrum, Basic, etc.). See the [Azure DevOps Integration guide](/docs/pm/azure-devops-integration) for step-by-step setup and troubleshooting. ### Asana ```bash TASK_TRACKER=asana ASANA_API_TOKEN=your-asana-pat # ASANA_DEFAULT_PROJECT_GID=2222222222222222 # optional, first project if omitted ``` Create a token at [https://app.asana.com/0/developer-console](https://app.asana.com/0/developer-console). **Project GID**: the numeric ID after `/project/` in your project URL (e.g. `https://app.asana.com/1/…/project/2222222222222222/list/…` → `2222222222222222`). See the [Asana Integration guide](/docs/pm/asana-integration) for step-by-step setup and troubleshooting. ### GitHub Issues @devintern/pm creates issues in a repository via the GitHub REST API. ```bash TASK_TRACKER=github GITHUB_TOKEN=ghp_xxxxxxxxxxxx GITHUB_REPO=your-username-or-org/your-repo ``` **Personal Access Token**: both types work; fine-grained is recommended: - **Fine-grained:** [Generate token](https://github.com/settings/personal-access-tokens/new) with **Issues: Read and write** on the target repo - **Classic:** [Generate token](https://github.com/settings/tokens/new) with `repo` scope (private repos) or `public_repo` (public repos only) See the [GitHub Issues Integration guide](/docs/pm/github-integration) for step-by-step setup, label mapping, and troubleshooting. ### Markdown (local file export) ```bash TASK_TRACKER=markdown # MARKDOWN_TASKS_DIR=.devintern-pm/tasks # optional, defaults to .devintern-pm/tasks ``` Tasks are written as Markdown files under this directory (relative to the project root). ## Agent Harness Configure which AI agent CLI runs when generating stories and tasks: ```bash # Which harness to use (default: claude-code) AGENT_HARNESS=claude-code # Optional: path to the agent executable (leave unset in most cases) # AGENT_CLI_PATH=/custom/path/to/claude ``` In most cases you only need `AGENT_HARNESS`. By default each harness uses its standard command (for example `claude` for `claude-code`), and devintern locates it on your `PATH` automatically. Set `AGENT_CLI_PATH` only when the CLI is not on your `PATH` or uses a non-standard name. **Resolution order for the executable path:** 1. `AGENT_CLI_PATH` 2. Harness-specific env var (e.g. `OPENCODE_CLI_PATH` when `AGENT_HARNESS=opencode`) 3. Harness default command, located on your `PATH` (e.g. `claude`) Common `AGENT_HARNESS` values include `claude-code`, `opencode`, `codex`, `cursor`, `grok`, `deepseek`, `antigravity`, `cline`, `goose`, `kilo-code`, `kimi`, and `qwen`. If you do need to set a path explicitly, run `which` for the harness binary (`claude`, `opencode`, `codex`, `cursor-agent`, `grok`, `reasonix`, `agy`, `cline`, `goose`, `kilo`, `kimi`, or `qwen`). **Cursor note:** The Cursor harness uses Cursor's headless `cursor-agent` CLI (not a command named `cursor`). Cursor also installs an `agent` alias, but devpm looks for `cursor-agent` because other tools use the `agent` name too. Install Cursor and enable the CLI from Cursor's settings, then set `AGENT_HARNESS=cursor`. **Grok note:** Product name is Grok Build; the CLI binary is `grok`. Install from [x.ai/cli](https://x.ai/cli), authenticate (browser login or `XAI_API_KEY`), then set `AGENT_HARNESS=grok`. **DeepSeek note:** Harness id is `deepseek`; the CLI binary is `reasonix` (DeepSeek-Reasonix). Install with `npm i -g reasonix`, set `DEEPSEEK_API_KEY` (or run `reasonix setup`), then set `AGENT_HARNESS=deepseek`. **Antigravity note:** Harness id is `antigravity` (alias `agy`); the CLI binary is `agy`. Google retired consumer Gemini CLI on 2026-06-18 in favor of Antigravity CLI. Install from [antigravity.google/docs/cli/install](https://antigravity.google/docs/cli/install), authenticate (browser/keyring, or `ANTIGRAVITY_TOKEN` for CI), then set `AGENT_HARNESS=antigravity`. Legacy `AGENT_HARNESS=gemini` still routes to Antigravity with a deprecation warning. Prefer `AGENT_CLI_PATH` / `ANTIGRAVITY_CLI_PATH` / `AGY_CLI_PATH` over `GEMINI_CLI_PATH`. **Kilo Code note:** Harness id is `kilo-code`; the CLI binary is `kilo`. **Qwen note:** Qwen Code has no `--model` flag; pick the model in `~/.qwen/settings.json`. **Advanced spawn tuning** (rarely needed): ```bash # Retry attempts when the agent CLI path is momentarily missing (e.g. during an auto-update) # Default: 5 AGENT_SPAWN_ENOENT_RETRIES=5 # Initial backoff delay in milliseconds between retries (doubles each attempt) # Default: 1000 AGENT_SPAWN_ENOENT_BACKOFF_MS=1000 ``` These control how devpm handles a brief window where the agent CLI symlink is missing because the tool is updating itself. The defaults are sufficient for all common auto-updaters. ## Verbose API Logging Enable detailed API call logging for debugging: ```bash DEVINTERN_VERBOSE=1 ``` When set to `1` or `true`, every API request, response status, and retry attempt is printed to the console. This is useful for diagnosing authentication or connectivity issues. The same effect can be achieved at runtime by passing `--verbose` (or `-v`) to `devpm`. ## No License Required @devintern/pm is free to use under the FSL license: it performs no license check, trial gate, or `LICENSE_KEY` validation. Just run `devpm init` and start creating tasks. ## Environment File Location @devintern/pm searches for `.devintern-pm/.env` by traversing up from the current working directory to the project root (the nearest `.git` directory or your home directory). You can run `devpm` from any subdirectory of your project and it will find the correct config automatically. Run `devpm init` once per project to create this file (guided wizard in a terminal, or `devpm init --yes` for the template). ## Troubleshooting **"Missing required environment variables"** - Make sure you've run `devpm init` or copied `.env.example` to `.devintern-pm/.env` - Verify you've set the variables for your selected `TASK_TRACKER` (not every backend block) **"API error (401)"** - Verify your API token is correct for the selected backend - Check that your credentials match your account - For GitHub fine-grained tokens on org repos, confirm an admin has approved the token - For Linear, confirm `LINEAR_API_KEY` is the raw `lin_api_…` value (no `Bearer` prefix), see the [Linear Integration guide](/docs/pm/linear-integration#troubleshooting) - For Azure DevOps, confirm all three variables are set and the PAT has **Work Items (Read & write)**: see the [Azure DevOps Integration guide](/docs/pm/azure-devops-integration#troubleshooting) - For Jira, confirm `JIRA_EMAIL` matches the account that created the API token, see the [Jira Integration guide](/docs/pm/jira-integration#troubleshooting) **"GitHub API error (403)" or "(422)"** - See the [GitHub Issues Integration guide](/docs/pm/github-integration#troubleshooting) for token permissions, repository access, and label setup **"Unknown agent harness"** - Check `AGENT_HARNESS` spelling (use kebab-case, e.g. `claude-code`, `grok`, `deepseek`) - The error lists every valid harness name; pick one from that list - Ensure the matching CLI is installed and on your `PATH`, or set `AGENT_CLI_PATH` / `_CLI_PATH` --- # Usage ## Interactive Mode (Recommended) The interactive mode provides a step-by-step terminal UI for creating tasks. This is the recommended way to use @devintern/pm for all users: ```bash devpm --interactive ``` The interactive mode will guide you through: 1. **Source type selection**: Choose between Figma URL, error log, or free-form prompt 2. **Source input**: Enter your Figma URL, error log, or requirements 3. **Custom instructions** (optional): Add additional requirements or focus areas 4. **Epic linking** (optional): Link the story to an existing epic. This step is skipped for trackers that do not support a real epic/parent hierarchy (Trello, GitHub Issues, and Markdown). Supported by Jira, Linear, Azure DevOps, and Asana. 5. **Issue type** (Jira, Azure DevOps, GitHub, Markdown only): Select Story, Task, Bug, Epic, or enter a custom type. This step is skipped for Linear, Trello, and Asana, which do not support setting an issue type. 6. **Prompt style**: Choose between PM style or Technical style 7. **Confirmation**: Review your configuration before proceeding **Features:** - Step-by-step guided workflow - Keyboard navigation (Enter to confirm, ESC to go back, Ctrl+C to exit) - Header shows active tracker and project as `Tracker/Project` (e.g., `Jira/PROJ`, `Trello/My Board`) so you always know your context - Press `Ctrl+P` at any step to switch to a different project without restarting - Visual preview of your configuration - No need to remember command-line flags - Works great for both technical and non-technical users ## CLI Usage (For Power Users) For power users who prefer command-line flags: ```bash devpm --figma [options] devpm --log [options] devpm --prompt [options] ``` ### Source Options (one required) - `--figma `: Figma design node URL to analyze - `--log `: Error log or bug report text to analyze - `--prompt `: Free-form text describing requirements or features ### Additional Options - `--epic, -e `: Link the created story to an epic (e.g., PROJ-100). Ignored for trackers that do not support a real epic/parent hierarchy (Trello, GitHub Issues, Markdown). - `--type, -t `: Issue type (default: "Task"). Common types: Task, Story, Bug, Epic. Only applied by backends that support issue types (Jira, Azure DevOps, GitHub, Markdown); ignored by Linear, Trello, and Asana. - `--custom, -c `: Additional custom instructions for the requirements - `--style, -s `: Prompt style: "pm" (default) or "technical" - **pm**: Focuses on user stories and acceptance criteria - **technical**: Includes Technical Considerations section - `--model, -m `: AI model to use (e.g., "sonnet", "opus", or full model name) - `--decompose`: Decompose the story into subtasks (default: off) - `--confirm`: Interactively confirm each subtask before creating - `--verbose, -v`: Enable verbose API logging for debugging (same as setting `DEVINTERN_VERBOSE=1`) - `--help, -h`: Show help message ### Examples **Figma designs:** > **Note**: Figma functionality requires the Figma MCP server to be installed and configured in your AI agent (Claude Code only). ```bash devpm --figma "https://www.figma.com/design/abc/file?node-id=123-456" devpm --figma "https://..." --epic PROJ-100 devpm --figma "https://..." -c "Focus on accessibility" devpm --figma "https://..." --style technical --decompose devpm --figma "https://..." --type Task ``` **Error logs:** ```bash devpm --log "Error: Cannot read property 'id' of undefined at line 42" devpm --log "$(cat error.log)" --epic PROJ-200 --type Bug devpm --log "Stack trace..." --style technical --model opus ``` **Free-form prompts:** ```bash devpm --prompt "Add user profile settings page with theme preferences" devpm --prompt "$(cat requirements.txt)" --epic PROJ-300 devpm --prompt "Implement OAuth login" --style technical --decompose ``` ## How It Works 1. **Input Analysis**: Your AI agent analyzes your input: - **Figma designs**: Uses the Figma MCP integration to extract design specifications - **Error logs**: Parses error messages and stack traces to identify root causes - **Free-form prompts**: Interprets requirements and feature descriptions 2. **Story Creation**: Creates a comprehensive story with: - User story format - Acceptance criteria - Technical considerations - Design notes (for Figma) or reproduction steps (for bugs) 3. **Epic Linking** (optional): Links the story to the specified epic. Only runs for trackers with a real epic/parent hierarchy (Jira, Linear, Azure DevOps, Asana). Skipped for Trello, GitHub Issues, and Markdown. 4. **Task Decomposition** (optional): Breaks down the story into subtasks that are: - Focused on single responsibilities - Completable within 1-2 days - Properly linked to the parent story --- # Jira Integration @devintern/pm creates Jira Cloud issues directly from AI-generated stories and tasks. Setup takes a few minutes: you need your site URL, account email, an API token, and a default project key. ## How It Works @devintern/pm uses the [Jira Cloud REST API v3](https://developer.atlassian.com/cloud/jira/platform/rest/v3/) to create issues in a project you configure. - New work items appear as **issues** in the target project with the issue type you select (Story, Task, Bug, Epic, etc.) - Descriptions are converted from markdown to **Atlassian Document Format (ADF)** so headings, lists, and inline formatting render in Jira - Subtasks are created as **Subtask** issue types linked to the parent issue - Epic links set the story's **parent** field to the epic issue key - Issue types are fetched from your Jira project's configuration: pick one that exists in your project This integration targets **Jira Cloud** (`*.atlassian.net`). Jira Data Center / Server is not supported. ## Setup ### 1. Set the backend In your `.devintern-pm/.env`: ```bash TASK_TRACKER=jira JIRA_BASE_URL=https://your-org.atlassian.net JIRA_EMAIL=your-email@example.com JIRA_API_TOKEN=your-api-token JIRA_DEFAULT_PROJECT_KEY=PROJ ``` All four Jira variables are required. `TASK_TRACKER` defaults to `jira` if omitted. ### 2. Find your Jira site URL Your site URL is the hostname you use to open Jira in the browser: ``` https://your-org.atlassian.net/jira/... └─ JIRA_BASE_URL ─┘ ``` Set `JIRA_BASE_URL` to the full URL **without** a trailing slash. `https://` is optional: @devintern/pm strips it automatically. ### 3. Create an API token 1. Go to [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens) 2. Click **Create API token**, add a label (e.g. `DevIntern`), and copy the token 3. Paste the token into `JIRA_API_TOKEN` Use the **email address** of the Atlassian account that owns the token for `JIRA_EMAIL`. Authentication is Basic auth (`email:token`). Store credentials in `.devintern-pm/.env`. That file should stay out of version control (`devpm init` adds it to `.gitignore`). ### 4. Set your default project key The project key is the short uppercase prefix on issue keys (e.g. `PROJ` in `PROJ-123`). Find it in: - Any issue URL: `https://your-org.atlassian.net/browse/PROJ-123` → `PROJ` - **Project settings → Details → Key** In interactive mode you can switch projects with **Ctrl+P** before confirming, but `JIRA_DEFAULT_PROJECT_KEY` is still required. ### 5. Create your first issue ```bash devpm --interactive ``` The issue type step lists types configured in your Jira project (Story, Task, Bug, Epic, or custom types from your scheme). ## Issue Types Available types depend on your Jira project and issue type scheme. @devintern/pm fetches non-subtask types from the project API. Pick a type that exists in your project. If you choose a type that is not available (e.g. **Epic** on a project that does not use epics), Jira may reject the request. ## What Gets Created | devpm concept | Jira object | | ------------------------- | ------------------------------------------------ | | Story / Bug / Task / Epic | Issue of the selected type in the target project | | Subtask | Subtask issue linked to the parent issue | | Epic link | Parent field set to the epic issue key | When linking to an epic, enter the epic's issue key (e.g. `PROJ-42` from `.../browse/PROJ-42`). ## Troubleshooting **"Jira backend selected but … configuration is missing"** Set all four variables in `.devintern-pm/.env`: `JIRA_BASE_URL`, `JIRA_EMAIL`, `JIRA_API_TOKEN`, and `JIRA_DEFAULT_PROJECT_KEY`. **"Jira API error (401)"** - API token is invalid or revoked: create a new token at [Atlassian API tokens](https://id.atlassian.com/manage-profile/security/api-tokens) - `JIRA_EMAIL` does not match the account that created the token - Confirm the token was copied without extra spaces **"Jira API error (403)"** - Your account lacks permission to create issues in the target project - Ask a Jira admin to add you to the project with **Create issues** permission **"Jira API error (400)" when creating issues** - The selected issue type is not valid for this project: pick a type from the list in interactive mode - `JIRA_DEFAULT_PROJECT_KEY` is wrong: verify the key in **Project settings → Details** **Epic link fails** - Enter a valid epic issue key (e.g. `PROJ-100`), not a numeric ID - The epic must exist in a project your account can access - Some Jira configurations use **Epic Link** custom fields instead of parent: @devintern/pm sets the **parent** field (works with team-managed projects and modern company-managed epics) **Descriptions show unformatted markdown** Descriptions are converted to ADF before create. If formatting is missing, check that the description is standard markdown (headings, lists, bold): exotic syntax may not map to ADF. **Project picker (Ctrl+P) shows other projects but defaults elsewhere** Issues are created in the project selected in interactive mode, or in `JIRA_DEFAULT_PROJECT_KEY` when not overridden. --- # Trello Integration @devintern/pm creates Trello cards directly from AI-generated stories and tasks. Setup takes about a minute: you only need to generate an API token. ## How It Works @devintern/pm ships with a built-in Trello Power-Up, so you don't need to register your own app. You just authorize your Trello account once and paste the token into your config. Each story becomes a card. Subtasks become checklist items on the parent card. Issue type selection and epic linking are both skipped: Trello does not expose Jira-style issue types or a native parent hierarchy. ## Setup ### 1. Set the backend In your `.devintern-pm/.env`: ```bash TASK_TRACKER=trello ``` ### 2. Generate your API token Run `devpm`. If `TRELLO_API_TOKEN` is missing, it will print a direct authorization URL. Visit the URL, click **Allow**, and copy the token from the next page. Alternatively, you can generate the token manually at any time: ``` https://trello.com/1/authorize?expiration=never&scope=read,write&response_type=token&name=DevIntern&key=b2d5d1ced28b515c6eb66c40187400b0 ``` > The `expiration=never` parameter creates a long-lived token so you don't need to re-authorize periodically. You can revoke it at any time from your [Trello account settings](https://trello.com/u/me/account). ### 3. Add the token to your config ```bash TRELLO_API_TOKEN=your-generated-token ``` That's it. Run `devpm --interactive` to create your first card. ## Optional Configuration ### Default board and list Without these set, @devintern/pm uses your first board and first list. To pin a specific destination: ```bash TRELLO_DEFAULT_BOARD_ID=abc123 TRELLO_DEFAULT_LIST_NAME="To Do" ``` **Finding your board ID:** Open the board in Trello and look at the URL, `trello.com/b/{boardId}/{board-name}`. The short alphanumeric segment is the ID (e.g. `abc123` in `trello.com/b/abc123/My-Board`). **List name:** Use the exact list title as shown on the board (e.g. `"To Do"`, `"Backlog"`). If the name doesn't match any list on the board, @devintern/pm falls back to the first list. ### Using your own Power-Up If you need isolated rate limits (enterprise use, high-volume automation), register your own Power-Up: 1. Go to [trello.com/power-ups/admin](https://trello.com/power-ups/admin) and create a new Power-Up 2. Navigate to **API Key** tab → **Generate a new API Key** 3. Add to your config: ```bash TRELLO_API_KEY=your-own-api-key TRELLO_API_TOKEN=your-api-token ``` When `TRELLO_API_KEY` is set, it overrides the bundled key. ## What Gets Created | devpm concept | Trello object | | ------------------ | --------------------------------- | | Story / Task / Bug | Card in the target list | | Subtask | Checklist item on the parent card | | Epic link | Not supported (step is skipped) | ## Troubleshooting **"Trello backend requires TRELLO_API_TOKEN"** Visit the authorization URL printed in the error, click Allow, and add the resulting token to your `.devintern-pm/.env`. **"No Trello boards found"** Your token authorized successfully but the account has no boards. Create at least one board in Trello first. **"No lists found on Trello board"** The target board exists but has no lists. Trello boards need at least one list before cards can be created. **API 401 errors after working previously** Your token was revoked. Go to [Trello account settings → Applications](https://trello.com/u/me/account) to check. Re-run the authorization flow to generate a new token. **Rate limit errors (429)** @devintern/pm uses the shared Power-Up key, which allows 300 requests per 10 seconds across all users. For a typical `devpm` run (3–5 API calls), this limit is never reached in practice. If you hit it under heavy automation, switch to your own Power-Up key via `TRELLO_API_KEY`. --- # GitHub Issues Integration @devintern/pm creates GitHub Issues directly from AI-generated stories and tasks. Setup takes a few minutes: you need a Personal Access Token and a target repository. ## How It Works @devintern/pm uses the [GitHub REST Issues API](https://docs.github.com/en/rest/issues) to create and update issues in a repository you configure. - New issues appear under the repo's **Issues** tab - Stories, bugs, tasks, and epics map to issue labels (see below) - Subtasks become linked issues with a task list on the parent issue - Epic linking is not supported: GitHub Issues has no native parent hierarchy, so the epic linking step is skipped in interactive mode and the `--epic` flag is ignored ## Setup ### 1. Set the backend In your `.devintern-pm/.env`: ```bash TASK_TRACKER=github GITHUB_REPO=your-username-or-org/your-repo ``` `GITHUB_REPO` is the repository in `owner/repo` form (e.g. `acme/my-app`). All issues are created in this repository. ### 2. Create a Personal Access Token Both token types work. **Fine-grained tokens are recommended**: they grant only the permissions @devintern/pm needs on specific repositories. #### Fine-grained (recommended) 1. Go to [Fine-grained tokens → Generate new token](https://github.com/settings/personal-access-tokens/new) 2. Set **Repository access** to include your target repo (and any others you want visible in the Ctrl+P repo picker) 3. Under **Permissions → Repository permissions**, set: - **Issues:** Read and write - **Metadata:** Read (required; selected automatically) 4. Generate the token and copy it For organization-owned repositories, an org admin may need to **approve** the token before it works. #### Classic 1. Go to [Classic tokens → Generate new token](https://github.com/settings/tokens/new) 2. Select scopes: - **Private repositories:** `repo` (full control of private repositories) - **Public repositories only:** `public_repo` 3. Generate the token and copy it Classic tokens with `repo` work but grant broader access than necessary. ### 3. Add the token to your config ```bash GITHUB_TOKEN=ghp_xxxxxxxxxxxx ``` Run `devpm --interactive` to create your first issue. ## Issue Types and Labels When you pick an issue type in @devintern/pm, it applies a GitHub label: | devpm issue type | GitHub label | | ---------------- | ------------- | | Story | `enhancement` | | Bug | `bug` | | Task | `task` | | Epic | `epic` | New repositories include `bug` and `enhancement` by default. Create `task` and `epic` labels in your repo if you use those issue types: otherwise GitHub may reject the request when applying a missing label. ## What Gets Created | devpm concept | GitHub object | | ------------------------- | ------------------------------------------------------------- | | Story / Bug / Task / Epic | Issue with title, body, and mapped label | | Subtask | New issue linked from a `## Subtasks` task list on the parent | | Epic link | Not supported (step is skipped) | ## Troubleshooting **"Missing required environment variables" / GitHub configuration errors** Set both `GITHUB_TOKEN` and `GITHUB_REPO` (as `owner/repo`) in `.devintern-pm/.env`. **"GitHub API error (401)"** - Token is invalid or expired: generate a new one - For fine-grained tokens on org repos, check whether an admin still needs to approve the token **"GitHub API error (403)"** - Token lacks **Issues: Read and write** (fine-grained) or `repo` / `public_repo` (classic) - Token does not have access to the configured repository - Your account lacks permission to create issues in that repo **"GitHub API error (422)" when creating issues** - A mapped label (`task`, `epic`, etc.) does not exist in the repository: create it under **Issues → Labels**, or pick an issue type whose label already exists **Repo picker (Ctrl+P) shows other repos but issues go elsewhere** Issues are always created in the repository set by `GITHUB_REPO`. The picker lists repositories your token can access for reference; changing the selection does not redirect issue creation yet. --- # Asana Integration @devintern/pm creates Asana tasks directly from AI-generated stories and tasks. Setup takes a few minutes: you need a Personal Access Token and optionally a default project. ## How It Works @devintern/pm uses the [Asana REST API](https://developers.asana.com/reference/rest-api-reference) to create tasks in a project you configure. - New work items appear as **tasks** in the target project (descriptions use Asana rich text via `html_notes`, not raw markdown in `notes`) - Subtasks are created as Asana subtasks on the parent task - Epic links use Asana's parent-task relationship - Issue type selection is skipped: Asana does not expose Jira-style issue types through this integration ## Setup ### 1. Set the backend In your `.devintern-pm/.env`: ```bash TASK_TRACKER=asana ASANA_API_TOKEN=your-asana-pat ``` ### 2. Create a Personal Access Token 1. Go to the [Asana developer console](https://app.asana.com/0/developer-console) 2. Create a new **Personal access token** 3. Copy the token into `ASANA_API_TOKEN` The token must be able to read projects and create tasks in the workspaces you use. ### 3. (Optional) Pin a default project If you omit `ASANA_DEFAULT_PROJECT_GID`, @devintern/pm uses the first accessible project. To always create tasks in a specific project, set: ```bash ASANA_DEFAULT_PROJECT_GID=2222222222222222 ``` #### Finding your project GID Open the project in Asana and copy the numeric ID that appears right after `/project/` in the URL: ``` https://app.asana.com/1/1111111111111111/project/2222222222222222/list/3333333333333333 └─ project GID ─┘ ``` Use `2222222222222222` as `ASANA_DEFAULT_PROJECT_GID`. The first number is the workspace ID; the last number is a view/section ID. Neither of those is the project GID. ### 4. Create your first task ```bash devpm --interactive ``` ## What Gets Created | devpm concept | Asana object | | ------------------ | ------------------------------------------------------------------------------------------- | | Story / Task / Bug | Task in the target project (markdown from devpm is converted to Asana HTML in `html_notes`) | | Subtask | Subtask under the parent task | | Epic link | Parent task relationship via `setParent` | ## Troubleshooting **"Asana backend selected but ASANA_API_TOKEN is missing"** Set `ASANA_API_TOKEN` in `.devintern-pm/.env` after creating a token in the [developer console](https://app.asana.com/0/developer-console). **"Asana API error (401)"** - Token is invalid or revoked: generate a new PAT - Confirm the token was copied without extra spaces **"Asana API error (403)"** - Your account may lack permission to create tasks in the target project - The project may be in a workspace the token cannot access **Tasks land in the wrong project** - Set `ASANA_DEFAULT_PROJECT_GID` to the numeric ID from that project's Asana URL - In interactive mode, pick the correct project before confirming **"Could not fetch projects" warning in interactive mode** - Check `ASANA_API_TOKEN` and network access to `app.asana.com` - Ensure your Asana account has at least one project; create one in the UI if needed --- # Linear Integration @devintern/pm creates Linear issues directly from AI-generated stories and tasks. Setup takes a few minutes: you need a Personal API key and optionally a default team. ## How It Works @devintern/pm uses the [Linear GraphQL API](https://developers.linear.app/docs/graphql/working-with-the-graphql-api) to create issues in a team you configure. - New work items appear as **issues** in the target team (descriptions are sent as markdown, which Linear renders natively) - Subtasks are created as sub-issues linked to the parent issue - Epic links use Linear's parent-issue relationship - Issue type selection is skipped: Linear does not expose Jira-style issue types through this integration ## Setup ### 1. Set the backend In your `.devintern-pm/.env`: ```bash TASK_TRACKER=linear LINEAR_API_KEY=lin_api_xxxxxxxxxxxx ``` ### 2. Create a Personal API key 1. Go to [Linear API settings](https://linear.app/settings/api) 2. Under **Personal API keys**, click **Create key** 3. Add a label (e.g. `DevIntern`) and copy the key into `LINEAR_API_KEY` The key starts with `lin_api_` and cannot be viewed again after you leave the page. Store it in `.devintern-pm/.env`. That file should stay out of version control (`devpm init` adds it to `.gitignore`). The key inherits your Linear account permissions: you can create issues in any team you belong to. ### 3. (Optional) Pin a default team If you omit `LINEAR_DEFAULT_TEAM_KEY`, @devintern/pm uses the first accessible team. To always create issues in a specific team, set: ```bash LINEAR_DEFAULT_TEAM_KEY=ENG ``` #### Finding your team key The team key is the short prefix before the issue number in identifiers like `ENG-42` or `DES-7`. You can find it in either place: - **From an issue:** open any issue in the team. The identifier prefix is the team key (`ENG` in `ENG-42`) - **From team settings:** Linear → **Settings** → **Teams** → select your team → **Key** In interactive mode you can also pick a team with **Ctrl+P** before confirming. ### 4. Create your first issue ```bash devpm --interactive ``` ## What Gets Created | devpm concept | Linear object | | ------------------ | ---------------------------------------- | | Story / Task / Bug | Issue in the target team | | Subtask | Sub-issue linked to the parent issue | | Epic link | Parent issue relationship via `parentId` | ## Troubleshooting **"Linear backend selected but LINEAR_API_KEY is missing"** Set `LINEAR_API_KEY` in `.devintern-pm/.env` after creating a key at [Linear API settings](https://linear.app/settings/api). **"Linear API error (401)"** - API key is invalid or revoked: create a new Personal API key - Confirm the key was copied without extra spaces or a `Bearer` prefix (paste the raw `lin_api_…` value) **"No Linear teams found"** Your account has no teams yet, or the API key's user cannot see any. Create a team in Linear first, or ask a workspace admin to add you to one. **Issues land in the wrong team** - Set `LINEAR_DEFAULT_TEAM_KEY` to the key from that team's settings or issue identifiers - In interactive mode, pick the correct team with **Ctrl+P** before confirming **"Could not fetch teams" warning in interactive mode** - Check `LINEAR_API_KEY` and network access to `api.linear.app` - Ensure your Linear account belongs to at least one team --- # Azure DevOps Integration @devintern/pm creates Azure DevOps work items directly from AI-generated stories and tasks. Setup takes a few minutes: you need a Personal Access Token, your organization slug, and a target project. ## How It Works @devintern/pm uses the [Azure DevOps Work Items REST API](https://learn.microsoft.com/en-us/rest/api/azure/devops/wit/work-items) to create items in a project you configure. - New work items appear in the target **project** with the issue type you select (User Story, Bug, Task, etc.) - Descriptions are converted from markdown to **HTML** for `System.Description` (Azure DevOps defaults to HTML; raw markdown would display as plain text) - Subtasks are created as **Task** work items linked as children of the parent - Epic links use Azure DevOps parent/child hierarchy relationships - Work item IDs are numeric (e.g. `123`): use the ID from the work item URL when linking epics This integration targets **Azure DevOps Services** (`dev.azure.com`). Self-hosted Azure DevOps Server is not supported. ## Setup ### 1. Set the backend In your `.devintern-pm/.env`: ```bash TASK_TRACKER=azure-devops AZURE_DEVOPS_ORG=your-organization AZURE_DEVOPS_PAT=your-pat-token AZURE_DEVOPS_PROJECT=YourProject ``` All three Azure DevOps variables are required. ### 2. Find your organization slug Open your organization in the browser. The slug is the segment after `dev.azure.com/`: ``` https://dev.azure.com/contoso/MyProject/_workitems/edit/123 └─ org ─┘ ``` Set `AZURE_DEVOPS_ORG=contoso`: the slug only, not the full URL. ### 3. Create a Personal Access Token 1. Go to [Personal Access Tokens](https://dev.azure.com/your-org/_usersSettings/tokens) (replace `your-org` with your organization slug) 2. Click **+ New Token** 3. Set a name (e.g. `DevIntern`) and choose an expiration 4. Under **Scopes**, enable: - **Work Items:** Read & write - **Project and Team:** Read 5. Under **Organizations**, select your org (or **All accessible organizations** if you use multiple) 6. Create the token and copy it into `AZURE_DEVOPS_PAT` Store the PAT in `.devintern-pm/.env`. That file should stay out of version control (`devpm init` adds it to `.gitignore`). ### 4. Set your default project `AZURE_DEVOPS_PROJECT` must match the project **name** exactly as shown in Azure DevOps, not the project ID or description. Find it in the URL when you open a board or backlog: ``` https://dev.azure.com/contoso/MyProject/_boards/board/t/... └─ project name ─┘ ``` Or copy the name from **Project settings → Overview**. In interactive mode you can switch projects with **Ctrl+P** before confirming, but `AZURE_DEVOPS_PROJECT` is still required as the default. ### 5. Create your first work item ```bash devpm --interactive ``` The issue type step lists work item types from your project's process template (Agile, Scrum, Basic, CMMI, or a custom process). ## Issue Types Available types depend on your Azure DevOps process template: | Process template | Common types | | ---------------- | ---------------------------------------------- | | Agile | Epic, Feature, User Story, Task, Bug | | Scrum | Epic, Feature, Product Backlog Item, Task, Bug | | Basic | Epic, Issue, Task | | CMMI | Epic, Feature, Requirement, Task, Bug | @devintern/pm fetches the types configured in your project. Pick a type that exists in your process. If you choose a type that is not defined, work item creation will fail. ## What Gets Created | devpm concept | Azure DevOps object | | ------------------------- | ------------------------------------------------------------------------------------- | | Story / Bug / Task / Epic | Work item of the selected type in the target project (markdown → HTML in Description) | | Subtask | **Task** work item linked as a child of the parent | | Epic link | Parent/child hierarchy link between work items | When linking to an epic, enter the parent work item's **numeric ID** (from the URL: `.../_workitems/edit/456` → `456`), not a Jira-style key like `PROJ-123`. ## Troubleshooting **"Azure DevOps backend selected but … configuration is missing"** Set all three variables in `.devintern-pm/.env`: `AZURE_DEVOPS_ORG`, `AZURE_DEVOPS_PAT`, and `AZURE_DEVOPS_PROJECT`. **"Azure DevOps API error (401)"** - PAT is invalid or expired: create a new token - Confirm the token was copied without extra spaces - Verify the token's organization scope includes the org in `AZURE_DEVOPS_ORG` **"Azure DevOps API error (403)"** - PAT lacks **Work Items (Read & write)** scope: regenerate with the correct permissions - Your account may not have permission to create work items in the target project: ask a project admin to grant **Contributors** access or equivalent **"Azure DevOps API error (404)" when creating work items** - `AZURE_DEVOPS_ORG` or `AZURE_DEVOPS_PROJECT` is wrong: check the slug and project name against your Azure DevOps URL - Project name is case-sensitive and must match exactly (including spaces) **Work item creation fails with an invalid work item type** - The selected type does not exist in your process template (e.g. choosing **Story** on a Scrum project that uses **Product Backlog Item**) - Re-run interactively and pick a type from the list, or use `--type` with a valid type name for your project **Descriptions show raw markdown syntax (e.g. `##`, `**bold**`)** Azure DevOps `System.Description` defaults to **HTML** via the REST API. @devintern/pm converts markdown to HTML before creating the work item. If you still see unrendered markdown on older work items, those were likely created before this conversion was added. Azure DevOps also supports an opt-in **Markdown** format (`/multilineFieldsFormat/System.Description` = `Markdown`) on organizations with the New Boards markdown editor. @devintern/pm uses HTML for compatibility with the default format. **Subtask creation fails** - Subtasks are always created as **Task** work items. If your process template does not include Task (unusual), subtask creation will fail - Confirm the parent key is a numeric work item ID that exists in the project **Epic link fails with "Work item not found"** - Azure DevOps uses numeric IDs, not Jira-style keys: enter the epic's work item ID (e.g. `456` from `.../_workitems/edit/456`) - Both the story and epic must exist in a project your PAT can access **Project picker (Ctrl+P) shows other projects but defaults elsewhere** Work items are created in the project selected in interactive mode, or in `AZURE_DEVOPS_PROJECT` when not overridden. Ensure the default project name matches the project you intend to use. ---