# AGENTS.md — Redmine CLI tooling This directory holds the `redmine-cli` Docker container — a thin Python CLI over `python-redmine` for Redmine issue tracking. Every Crush session that works on or with this tool should read this file first: how to invoke it, what commands exist, what gotchas to avoid, and the patterns to follow for common ticket operations. ## Connection & invocation Invoke the real container with `docker run`. Credentials come from the centralized credential store (see `tooling-cli/KNELCredsManager`): ```bash docker run --rm --env-file ~/.creds/redmine.env \ git.knownelement.com/reachableceo/redmine-cli:latest ``` There is intentionally **no `bin/` wrapper** — invoke the real container, as the project-wide rules require. For readability in an interactive session you may define a throwaway shell alias (do not commit one): ```bash alias redmine='docker run --rm --env-file ~/.creds/redmine.env \ git.knownelement.com/reachableceo/redmine-cli:latest' ``` The examples below write `redmine ` for brevity; expand it to the full `docker run` line (or set the alias) before running. | Item | Value | |------|-------| | Instance | `https://projects.knownelement.com` | | API user | Charles N (id 5, login `reachableceo`) | | Credentials | `~/.creds/redmine.env` (`REDMINE_URL`, `REDMINE_API_KEY`) | | CLI image | `git.knownelement.com/reachableceo/redmine-cli:latest` | | Source | `src/redmine_cli.py` in this directory | To use a locally built image instead of the registry image, either set `REDMINE_CLI_IMAGE` or swap the tag for `kneldevstack-aimiddleware-redmine-cli:latest`. ## Quick start ```bash # Connection sanity check (run this first in any session): redmine whoami # Look at a ticket: redmine show 314 # Your queue: redmine list --assigned-to-me ``` ## Command reference | Command | Description | |---------|-------------| | `whoami` | Authenticated user + connection test. | | `projects` | List projects (id, identifier, name). | | `statuses` | List issue statuses + which are "closed". | | `list` (`ls`) | List issues. Filters below. | | `show ` | Full issue detail incl. note history. | | `create` | Create an issue. | | `update ` | Update an issue (status, notes, assignee, done, ...). | | `close ` | Close (first closed status, done ratio 100%). | ### `list` filters - `-m, --assigned-to-me` — only issues assigned to you - `-a, --assigned-to ` — filter by assignee user id - `-p, --project ` — filter by project - `-s, --status ` — filter by status - `-l, --limit ` — max results (default 50) - `--sort ` — Redmine sort spec (default `priority:desc,updated_on:desc`) ### `create` options - `-p, --project ` **required** - `-s, --subject` **required** - `-d, --description` - `-a, --assigned-to ` - `-t, --tracker ` - `--priority ` - `--status ` ### `update` options - `-s, --status ` - `-n, --notes ` - `-a, --assigned-to ` - `--done-ratio <0-100>` - `--subject` - `--priority ` ## Key project / tracker / status IDs ### Most-used projects | ID | Identifier | Name | |----|------------|------| | 55 | `technicaloperations` | Known Element Enterprises - Technology & Facility Services | | 62 | `business-operations` | Known Element Enterprises - Business Services | | 77 | `tsys-group` | TSYS Group | Run `redmine projects` for the full list. ### Statuses | ID | Name | Closed? | |----|------|---------| | 1 | New | no | | 2 | In Progress | no | | 3 | Resolved | yes | | 4 | Feedback | no | | 5 | Closed | yes | | 6 | Rejected | no | ### Trackers Tracker 3 = **Support** (the most common one in project 55). Inspect a parent issue to inherit its exact tracker. ## Gotcha: creating subtasks `redmine create` has **no `--parent` flag**. To create a subtask of an existing issue you must use `python-redmine` directly inside the container (via `--entrypoint python`). Pattern (mount a script and run it in the same image): ```bash cat > /tmp/script.py <<'PY' import os from redminelib import Redmine rm = Redmine(os.environ["REDMINE_URL"].rstrip("/"), key=os.environ["REDMINE_API_KEY"]) issue = rm.issue.create( project_id=55, # inherit from parent tracker_id=3, # inherit from parent priority_id=2, # inherit from parent status_id=4, # Feedback = freshly created, awaiting work assigned_to_id=5, # inherit from parent parent_issue_id=314, # THE KEY FIELD subject="Your subject", description="Your description", ) print(f"Created #{issue.id}") PY docker run --rm --env-file ~/.creds/redmine.env \ --entrypoint python \ -v /tmp/script.py:/tmp/script.py \ git.knownelement.com/reachableceo/redmine-cli:latest \ /tmp/script.py ``` **Before creating subtasks**, inspect the parent to inherit its attributes: ```bash docker run --rm --env-file ~/.creds/redmine.env \ --entrypoint python \ git.knownelement.com/reachableceo/redmine-cli:latest -c " import os from redminelib import Redmine rm = Redmine(os.environ['REDMINE_URL'].rstrip('/'), key=os.environ['REDMINE_API_KEY']) i = rm.issue.get(PARENT_ID) print('project_id:', i.project.id) print('tracker_id:', i.tracker.id, i.tracker.name) print('priority_id:', i.priority.id, i.priority.name) print('assigned_to_id:', getattr(i.assigned_to,'id',None)) print('status_id:', i.status.id, i.status.name) " ``` ## Patterns ### Pattern: review & split a ticket 1. `redmine show ` — read the full ticket (description + history). 2. Identify the **distinct bodies of work**. Each should be independently trackable and assignable. 3. Map out **dependencies** between the pieces (what blocks what). 4. Propose a split to the user as a table: proposed subject, scope, and dependencies. Wait for approval before creating anything. 5. On approval: create subtasks (see "Gotcha: creating subtasks" above), inheriting project/tracker/priority/assignee from the parent. 6. Update the parent's **description** to an index table of children + recommended execution order. Add a note explaining the split. 7. Verify with `redmine show `. ### Pattern: ticket conventions - **Subject prefix:** OAM-pool tickets use `OAM: `. Match the parent's naming convention if one exists. - **Description header:** always start with `Parent/umbrella: # ().` - **Scope section:** bullet list of concrete deliverables. Use `- [ ]` for checklist items within a subtask. - **Dependencies section:** list what the ticket depends on and what it blocks, referencing ticket numbers once they exist. - **Newly created children** go to status **Feedback (4)** so they're visible but not yet "in progress". - **Tables** in descriptions render in Redmine's Markdown pipeline (`| a | b |`). ### Pattern: bulk operations For anything beyond a single `create`/`update`/`close`, write a Python script and run it inside the container as shown in the subtask gotcha above. This applies to: batch status changes, bulk ticket creation, inspecting parent attributes, relationship wiring, etc. ### Pattern: before you act - Always `redmine show ` before updating — confirm current status, assignee, and existing notes so you don't clobber context. - Never delete or close a ticket unless the user explicitly asks. - When in doubt about project/tracker/priority, inherit from the parent or ask the user. ## Git workflow ### Atomic commits Each commit is **one logical change** — one feature, one fix, one doc update. If you're tempted to write "and also..." in a commit message, that's a sign to split it into two commits. Stage precisely (`git add `, not `git add -A`) so unrelated changes don't get bundled. ### Conventional commit messages Use the [Conventional Commits](https://www.conventionalcommits.org/) format: ``` (): ``` Types used in this repo: `feat`, `fix`, `docs`, `refactor`, `chore`, `style`. Rules: - Subject line **under 72 chars**, lowercase, imperative mood. - No period at end of subject. - Body wrapped at 72 chars, explains **why** the change exists. ### Commit cadence - **Commit early and often.** Don't accumulate a pile of unrelated changes. - Commit **without asking** — if you made a coherent change, commit it. - Every task (ticket review, split, script addition, doc update) ends with the relevant files committed. - Run `git status` before committing to stage only what belongs together.