@odarino/backlog.md 1.54.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Backlog.md
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,433 @@
1
+ <p align="center">
2
+ <img src="./.github/backlog-logo.png" alt="Backlog.md logo" width="120">
3
+ </p>
4
+
5
+ <h1 align="center">Backlog.md</h1>
6
+ <p align="center"><strong>Markdown‑native Task Manager &amp; Kanban visualizer for any Git repository</strong></p>
7
+ <p align="center">AI agents write the code. You review the tasks: before, during, and after.</p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/@odarino/backlog.md"><img src="https://img.shields.io/npm/v/@odarino/backlog.md?color=brightgreen" alt="npm version"></a>
11
+ <a href="https://www.npmjs.com/package/@odarino/backlog.md"><img src="https://img.shields.io/npm/dm/@odarino/backlog.md" alt="npm downloads"></a>
12
+ <a href="https://github.com/odarino/Backlog.md/blob/main/LICENSE"><img src="https://img.shields.io/github/license/odarino/Backlog.md" alt="MIT license"></a>
13
+ <a href="https://github.com/odarino/Backlog.md"><img src="https://img.shields.io/github/stars/odarino/Backlog.md?style=social" alt="GitHub stars"></a>
14
+ </p>
15
+
16
+ <p align="center">
17
+ <code>npm i -g @odarino/backlog.md</code>
18
+ </p>
19
+
20
+ ## About this fork
21
+
22
+ This is a fork of [MrLesk/Backlog.md](https://github.com/MrLesk/Backlog.md) with extra web UI features. The command is still `backlog`.
23
+
24
+ > **Note:** Do not install this package and the original `backlog.md` globally at the same time. Both provide the `backlog` command. Uninstall one first: `npm uninstall -g backlog.md`.
25
+
26
+ Homebrew and Nix install the upstream package, which does not have the fork features.
27
+
28
+ ### Fork features
29
+
30
+ - **Image zoom.** Click an image in a task, a document, or a decision to open it full screen. Zoom with the mouse wheel, a double-click, or a pinch. Drag to pan. Use the arrow keys to move between all images of the task.
31
+ - **Image upload, paste, and drag-drop.** Paste a screenshot or drop image files into any task editor. The server compresses each image to WebP and saves it under `backlog/assets/images/<task-id>/`. Images that you add before a task has an ID go to `_unsorted/`, and they move to the task folder when you create the task. An image that another task also links is copied, not moved.
32
+ - **Image picker.** The image button in the editor toolbar opens a picker. Search, select, and insert existing images, or upload new ones.
33
+ - **Workflow editor.** In Settings, add, rename, reorder, color, and delete statuses. A rename or delete rewrites the affected tasks in `tasks/`, `completed/`, and `archive/tasks/`. Status colors show on the board columns and in the task list.
34
+ - **Clone task.** Copy a task into a new one from the task view (**Clone**), the CLI (`backlog task clone <id> [--title <title>]`), or MCP (`task_clone`). The copy keeps the content, labels, links, and checklists (unchecked), and resets the status (drafts stay drafts), due date, notes, summary, and comments.
35
+ - **Safer local server.** The web server rejects requests from other websites and DNS rebinding.
36
+
37
+ ### Configuration
38
+
39
+ | Key | Default | Range | Purpose |
40
+ |-----|---------|-------|---------|
41
+ | `image_max_dimension` | `1920` | 256–8192 | The longest side of a compressed image, in pixels |
42
+ | `image_quality` | `0.8` | 0.1–1.0 | WebP quality |
43
+ | `status_colors` | none | `#rrggbb` per status | Status colors, for example `{"Done":"#10b981"}` |
44
+
45
+ ### Limits
46
+
47
+ - Images over 50 megapixels are rejected. Shrink them first.
48
+ - GIF, SVG, and AVIF images are saved unchanged.
49
+ - With `auto_commit` on, a rename or delete of a status makes one commit for the task files and `config.yml`. A Settings save also commits `config.yml`.
50
+ - A running MCP server keeps the old status names until it restarts.
51
+ - Tasks that exist only on other git branches are not rewritten when you rename or delete a status.
52
+ - Two processes that change the workflow at the same moment can still conflict. Reload and try again.
53
+ - The web UI accepts only `http://127.0.0.1:<port>` and `http://localhost:<port>`. Access through a tunnel or a proxy is blocked.
54
+
55
+ ![Backlog demo GIF using: backlog board](./.github/backlog-v1.40.gif)
56
+
57
+
58
+ ---
59
+
60
+ > **Backlog.md** turns any folder into a **self‑contained project board**
61
+ > powered by plain Markdown files and a zero‑config CLI.
62
+
63
+ ## Why Backlog.md in the AI era
64
+
65
+ AI agents can now produce more plausible code in an hour than you can carefully read in a day.
66
+ The bottleneck is no longer writing code. It's your attention. You can't meaningfully review
67
+ 15,000 generated lines in one sitting, but you can read a screenful of task specs with acceptance
68
+ criteria before any code exists, and push back while a misunderstanding is still one sentence,
69
+ not a rebuilt feature.
70
+
71
+ Backlog.md structures agent work around **three review checkpoints**:
72
+
73
+ 1. **Review the spec:** the agent decomposes your idea into tasks with descriptions, acceptance
74
+ criteria, and milestones before implementation starts.
75
+ 2. **Review the plan:** the agent researches your codebase and writes its implementation plan
76
+ into the task. Approve it or steer before any code is written.
77
+ 3. **Review the code:** one task = one context window = one PR. Diffs stay a size a human can
78
+ actually read.
79
+
80
+ Afterwards, the completed tasks remain in Git as a permanent record of what was attempted and why,
81
+ legible to you, your team, and the next agent.
82
+
83
+ **Dogfooded:** nearly all of Backlog.md's own code is written by AI agents working through
84
+ Backlog.md itself. The full task ledger lives in this repo's [backlog folder](backlog/tasks).
85
+
86
+ 📺 **See it in action:** [Devoxx Belgium 2025](https://www.youtube.com/watch?v=LSoDQU_9MMA) · [AI Engineer Code Summit 2025](https://www.youtube.com/watch?v=zMXKhhwiCIc)
87
+
88
+ ## Features
89
+
90
+ * 🤖 **AI-ready** -- works with Claude Code, Gemini CLI, Codex, Kiro & any other MCP or CLI compatible AI assistant
91
+
92
+ * 📝 **Markdown-native tasks** -- every task is a plain `.md` file in your repo
93
+
94
+ * ✅ **Acceptance criteria & Definition of Done** -- verifiable scope per task, plus a reusable DoD checklist for every new task
95
+
96
+ * 🎯 **Milestones & dependencies** -- structure bigger efforts and make execution order reviewable, with task detail showing what a task waits on and what waits on it
97
+
98
+ * 📊 **Terminal Kanban** -- `backlog board` paints a live board in your shell; `backlog board export` creates shareable markdown reports
99
+
100
+ * 🌐 **Web UI** -- `backlog browser` serves a local Kanban board with drag-and-drop and task editing forms
101
+
102
+ * 🔍 **Search** -- fuzzy search across tasks, docs & decisions with `backlog search`
103
+
104
+ * 🔒 **Local-first** -- no server, no account, no telemetry; tasks are plain files in your repo, and remote Git operations are optional
105
+
106
+ * 💻 Cross-platform (macOS, Linux, Windows) · 🆓 MIT-licensed & open-source
107
+
108
+
109
+ ---
110
+
111
+ ## <img src="./.github/5-minute-tour-256.png" alt="Getting started" width="28" height="28" align="center"> Getting started
112
+
113
+ ```bash
114
+ # Install
115
+ npm i -g @odarino/backlog.md
116
+ # or: bun add -g @odarino/backlog.md
117
+
118
+ # Initialize in any Git repo
119
+ backlog init "My Awesome Project"
120
+
121
+ # Or initialize without Git for local/non-code projects
122
+ backlog init "Personal Planning" --no-git
123
+ ```
124
+
125
+ > [!TIP]
126
+ > **Running one-off with `npx`?** This tool's npm package is named `@odarino/backlog.md`, so use the full name: `npx @odarino/backlog.md init "My Project"`, `npx @odarino/backlog.md board`.
127
+ > Without an install, `npx backlog` resolves to an unrelated third-party npm package — not this tool.
128
+ > (With `@odarino/backlog.md` installed as a project dependency, `npx backlog` runs the local binary as usual.)
129
+
130
+ ### Run with Nix
131
+
132
+ > **Note:** This installs the upstream package, without the fork features.
133
+
134
+ Run Backlog.md directly from the repository flake:
135
+
136
+ ```bash
137
+ nix run github:MrLesk/Backlog.md -- --version
138
+ ```
139
+
140
+ Or install the named package into your Nix profile:
141
+
142
+ ```bash
143
+ nix profile install github:MrLesk/Backlog.md#backlog-md
144
+ ```
145
+
146
+ The Nix flake supports `x86_64-linux`, `aarch64-linux`, and
147
+ `aarch64-darwin`. The x86_64 Linux package uses Bun's baseline runtime so it
148
+ also works on pre-AVX2 processors with AVX support. Intel macOS users can use
149
+ the npm, Bun, or Homebrew installation instead.
150
+
151
+ The init wizard will ask how you want to connect AI tools:
152
+ - **CLI instructions** (recommended): creates a short instruction file that tells agents to run `backlog instructions overview`.
153
+ - **MCP connector**: optionally auto-configures Claude Code, Codex, Gemini CLI, Kiro or Cursor for teams that prefer MCP.
154
+ - **Skip**: no AI setup; use Backlog.md purely as a task manager.
155
+
156
+ For Cursor with CLI instructions, select AGENTS.md or pass `--agent-instructions cursor`; both use the same AGENTS.md target. Backlog.md preserves existing AGENTS.md content and does not migrate or remove unrelated user-managed `.cursor/rules` files.
157
+
158
+ Everything is stored as human-readable Markdown in a project-local backlog folder such as `backlog/`, `.backlog/`, or a custom project-relative path configured through `backlog.config.yml` (e.g. `backlog_directory: my-backlog`). Task IDs use a configurable prefix (`backlog init --task-prefix`): the default produces `TASK-1`-style IDs, while this repository uses `back`, so examples below show `BACK-1`-style IDs. Git is optional: `backlog init --no-git` creates a filesystem-only project.
159
+
160
+ ---
161
+
162
+ ## Working with AI agents
163
+
164
+ This is the recommended flow for Claude Code, Codex, Gemini CLI, Kiro and similar tools, following the **spec‑driven AI development** approach.
165
+ After running `backlog init`, agents should start by running `backlog instructions overview`. Work in this loop:
166
+
167
+ **Step 1: Describe your idea.** Tell the agent what you want to build and ask it to split the work into small tasks with clear descriptions and acceptance criteria.
168
+
169
+ **🤖 Ask your AI Agent:**
170
+ > I want to add a search feature to the web view that searches tasks, docs, and decisions. Please decompose this into small Backlog.md tasks.
171
+
172
+ > [!NOTE]
173
+ > **Review checkpoint #1:** read the task descriptions and acceptance criteria.
174
+
175
+ **Step 2: One task at a time.** Work on a single task per agent session, one PR per task. Good task splitting means each session can work independently without conflicts. Make sure each task is small enough to complete in a single conversation. You want to avoid running out of context window.
176
+
177
+ **Step 3: Plan before coding.** Ask the agent to research and write an implementation plan in the task. Do this right before implementation so the plan reflects the current state of the codebase.
178
+
179
+ **🤖 Ask your AI Agent:**
180
+ > Work on BACK-10 only. Research the codebase and write an implementation plan in the task. Wait for my approval before coding.
181
+
182
+ > [!NOTE]
183
+ > **Review checkpoint #2:** read the plan. Does the approach make sense? Approve it or ask the agent to revise.
184
+
185
+ **Step 4: Implement and verify.** Let the agent implement the task.
186
+
187
+ > [!NOTE]
188
+ > **Review checkpoint #3:** review the code, run tests, check linting, and verify the results match your expectations.
189
+
190
+ Mark verified work Done (or the configured final status). During periodic cleanup, use `backlog task complete` to move it off the board while preserving its record and dependency links. Use `backlog task archive` for canceled, duplicate, or invalid work; it removes incoming dependencies and task references.
191
+
192
+ If the output is not good enough: clear the plan/notes/final summary, refine the task description and acceptance criteria, and run the task again in a fresh session.
193
+
194
+ ---
195
+
196
+ ## Working without AI agents
197
+
198
+ Use Backlog.md as a standalone task manager from the terminal or browser.
199
+
200
+ ```bash
201
+ # Create and refine tasks
202
+ backlog task create "Render markdown as kanban"
203
+ backlog task edit BACK-1 -d "Detailed context" --ac "Clear acceptance criteria"
204
+
205
+ # Track work
206
+ backlog task list -s "To Do"
207
+ backlog task list --json | jq '.tasks[] | .id'
208
+ backlog task edit BACK-1 --comment "Can we split the UI work into a separate PR?" --comment-author @sara
209
+ backlog search "kanban"
210
+ backlog board
211
+
212
+ # Work visually in the browser
213
+ backlog browser
214
+ ```
215
+
216
+ You can switch between AI-assisted and manual workflows at any time; both operate on the same Markdown task files. Just prefer Backlog.md commands (CLI/MCP/Web) over hand-editing task files, so field types and metadata stay consistent.
217
+
218
+ Read commands support stable, versioned JSON for scripts and integrations. Use `--json` with `task list`, `task view`, the `task <id>` shorthand, and `search`. JSON mode is noninteractive and keeps successful stdout machine-readable. Add `--watch` to `task list --json` for an initial full list followed by changed full replacements, using the exact same JSON format. Read successive complete JSON values; each response replaces the previous list.
219
+
220
+ **Learn more:** [CLI reference](CLI-INSTRUCTIONS.md) | [Advanced configuration](ADVANCED-CONFIG.md)
221
+
222
+ ---
223
+
224
+ ## <img src="./.github/web-interface-256.png" alt="Web Interface" width="28" height="28" align="center"> Web Interface
225
+
226
+ Launch a web interface for visual task management on the local machine. The server listens on `127.0.0.1` and is not
227
+ reachable from other devices on the LAN or VPN:
228
+
229
+ ```bash
230
+ # Start the web server (opens browser automatically)
231
+ backlog browser
232
+
233
+ # Custom port
234
+ backlog browser --port 8080
235
+
236
+ # Don't open browser automatically
237
+ backlog browser --no-open
238
+ ```
239
+
240
+ **Features:**
241
+ - Interactive Kanban board with drag-and-drop
242
+ - Multi-select cards to move several tasks to one column
243
+ - Task creation and editing with forms
244
+ - Interactive acceptance criteria editor with checklists
245
+ - Real-time updates across all views
246
+ - Responsive design for desktop and mobile
247
+ - Task archiving with confirmation dialogs
248
+ - Seamless CLI integration - all changes sync with markdown files
249
+
250
+ ![Web Interface Screenshot](./.github/web.jpeg)
251
+
252
+ To keep the Web UI running as an auto-starting local service, see [Running Backlog.md as a Service](backlog/docs/doc-003%20-%20Running-Backlog-Browser-as-a-Service.md).
253
+
254
+ ---
255
+
256
+ ## 🔧 MCP Integration (Model Context Protocol)
257
+
258
+ CLI instructions are the default AI setup. MCP remains supported for AI coding assistants like Claude Code, Codex, Gemini CLI and Kiro when you explicitly prefer an MCP connector.
259
+ You can run `backlog init` (even if you already initialized Backlog.md) and choose MCP integration, or follow the manual steps below.
260
+
261
+ ### Client guides
262
+
263
+ <details>
264
+ <summary><strong>Claude Code</strong></summary>
265
+
266
+ ```bash
267
+ claude mcp add backlog --scope user -- backlog mcp start
268
+ ```
269
+
270
+ </details>
271
+
272
+ <details>
273
+ <summary><strong>Codex</strong></summary>
274
+
275
+ ```bash
276
+ codex mcp add backlog -- backlog mcp start
277
+ ```
278
+
279
+ </details>
280
+
281
+ <details>
282
+ <summary><strong>Gemini CLI</strong></summary>
283
+
284
+ ```bash
285
+ gemini mcp add backlog -s user backlog mcp start
286
+ ```
287
+
288
+ </details>
289
+
290
+ <details>
291
+ <summary><strong>Kiro</strong></summary>
292
+
293
+ ```bash
294
+ kiro-cli mcp add --scope global --name backlog --command backlog --args mcp,start
295
+ ```
296
+
297
+ </details>
298
+
299
+ <details>
300
+ <summary><strong>Cursor / other MCP clients</strong></summary>
301
+
302
+ Use the manual JSON config below in your client's MCP settings.
303
+
304
+ </details>
305
+
306
+ Use the shared `backlog` server name everywhere. The server finds the active project from your client's MCP roots, and re-resolves when you switch workspace or worktree. A single user-scope server covers every repo.
307
+
308
+ <details>
309
+ <summary><strong>Manual config</strong></summary>
310
+
311
+ ```json
312
+ {
313
+ "mcpServers": {
314
+ "backlog": {
315
+ "command": "backlog",
316
+ "args": ["mcp", "start"],
317
+ "env": {
318
+ "BACKLOG_CWD": "/absolute/path/to/your/project"
319
+ }
320
+ }
321
+ }
322
+ }
323
+ ```
324
+
325
+ Set `BACKLOG_CWD` to pin the server to one project and stop workspace following. Use it to always target the same backlog, or when your client can't report MCP roots.
326
+ If your IDE supports custom args but not env vars, you can also use `["mcp", "start", "--cwd", "/absolute/path/to/your/project"]`.
327
+ Until the server finds an initialized project, it serves `backlog://init-required`.
328
+
329
+ </details>
330
+
331
+ > [!IMPORTANT]
332
+ > When adding the MCP server manually, add a short instruction to your CLAUDE.md/AGENTS.md files telling agents to read `backlog://workflow/overview`.
333
+ > This step is not required when using `backlog init` as it adds these instructions automatically.
334
+ > For CLI-based setups, use `backlog instructions overview` to fetch the current workflow guidance.
335
+
336
+
337
+ Once connected, agents can read the Backlog.md workflow instructions via `backlog://workflow/overview`, with detailed guides at `backlog://workflow/task-creation`, `backlog://workflow/task-execution`, and `backlog://workflow/task-finalization`.
338
+ Use `/mcp` command in your AI tool (Claude Code, Codex, Kiro) to verify if the connection is working.
339
+
340
+ ---
341
+
342
+ ## <img src="./.github/cli-reference-256.png" alt="CLI Reference" width="28" height="28" align="center"> CLI reference
343
+
344
+ Full command reference covering task management, search, board, docs, decisions, and more: **[CLI-INSTRUCTIONS.md](CLI-INSTRUCTIONS.md)**
345
+
346
+ Quick examples: `backlog`, `backlog instructions`, `backlog task create`, `backlog task list`, `backlog task edit`, `backlog milestone add`, `backlog milestone rename`, `backlog milestone remove`, `backlog search`, `backlog board`, `backlog browser`.
347
+
348
+ Full help: `backlog --help`
349
+
350
+ ---
351
+
352
+ ## <img src="./.github/configuration-256.png" alt="Configuration" width="28" height="28" align="center"> Configuration
353
+
354
+ Backlog.md works with zero configuration. Settings merge from CLI flags, then the project config file (`backlog.config.yml` when present, otherwise `backlog/config.yml` or `.backlog/config.yml`), then built‑in defaults.
355
+
356
+ Run `backlog config` with no arguments to launch the interactive wizard (the same experience triggered from `backlog init` advanced setup). It walks through cross-branch accuracy (`checkActiveBranches`, `remoteOperations`, `activeBranchDays`), Git workflow (`autoCommit`, `bypassGitHooks`), ID formatting (`zeroPaddedIds`), editor integration (`defaultEditor`), Definition of Done defaults, and Web UI defaults (`defaultPort`, `autoOpenBrowser`). Skipping the wizard applies the safe built-in defaults, and rerunning `backlog init` or `backlog config` pre-populates prompts with your current values.
357
+
358
+ For filesystem-only projects (`backlog init --no-git`), the saved config forces `checkActiveBranches=false`, `remoteOperations=false`, and `autoCommit=false` so CLI, Web, and MCP local-file workflows do not depend on a Git repository.
359
+
360
+ ### Definition of Done defaults
361
+
362
+ Set project-wide DoD items with `backlog config` (or during `backlog init` advanced setup), in the Web UI (Settings → Definition of Done Defaults), or by editing the project config file directly:
363
+
364
+ ```yaml
365
+ definition_of_done:
366
+ - Tests pass
367
+ - Documentation updated
368
+ - No regressions introduced
369
+ ```
370
+
371
+ When a project uses root config discovery, edit `backlog.config.yml` instead of `backlog/config.yml`.
372
+
373
+ These items are added to every new task by default. You can add more on create with `--dod`, or disable defaults per task with `--no-dod-defaults`.
374
+
375
+ For the full configuration reference (all options, default values, commands, and detailed notes), see **[ADVANCED-CONFIG.md](ADVANCED-CONFIG.md)**.
376
+
377
+ ---
378
+
379
+ ## Troubleshooting
380
+
381
+ ### Apple Silicon (macOS)
382
+
383
+ On M-series Macs, `backlog` can fail with `illegal hardware instruction` or `Binary package not installed for darwin-...` when Node, Bun, or Homebrew run under Rosetta (x64 emulation) and install the Intel binary instead of the arm64 one — or the other way around. The launcher runs whichever darwin variant (arm64 or x64) is actually installed, but a clean native-arch install is the reliable fix.
384
+
385
+ Check what your tools report:
386
+
387
+ ```bash
388
+ uname -m # arm64 = Apple Silicon hardware; x86_64 = Intel or a Rosetta shell
389
+ node -p process.arch # architecture of your Node/Bun runtime
390
+ sysctl -in sysctl.proc_translated # 1 = current shell runs under Rosetta
391
+ which brew # /opt/homebrew = arm64 brew, /usr/local = Intel brew
392
+ ```
393
+
394
+ If the architectures disagree, reinstall with the native one:
395
+
396
+ > **Note:** The Homebrew formula installs the upstream package, without the fork features.
397
+
398
+ ```bash
399
+ # Homebrew: make sure `which brew` prints /opt/homebrew, then
400
+ brew reinstall backlog-md
401
+ ```
402
+
403
+ To keep the fork features, use npm or Bun:
404
+
405
+ ```bash
406
+ # npm
407
+ arch -arm64 npm i -g @odarino/backlog.md
408
+
409
+ # Bun
410
+ arch -arm64 bun add -g @odarino/backlog.md
411
+ ```
412
+
413
+ Running an x64 Node under Rosetta on purpose also works: `backlog` falls back to whichever `@odarino/backlog.md-darwin-*` package is present.
414
+
415
+ ---
416
+
417
+ ## 📺 Talks & Community
418
+
419
+ Watch Backlog.md in action:
420
+
421
+ - **Devoxx Belgium 2025**: [the spec-driven agent workflow behind Backlog.md, live on stage](https://www.youtube.com/watch?v=LSoDQU_9MMA)
422
+ - **AI Engineer Code Summit 2025**: [Backlog.md: task management for AI agents](https://www.youtube.com/watch?v=zMXKhhwiCIc)
423
+ - Slides from these and more talks: [mrlesk.com/talks](https://mrlesk.com/talks)
424
+
425
+ ### Community tools
426
+
427
+ - **[vscode-backlog-md](https://marketplace.visualstudio.com/items?itemName=ysamlan.vscode-backlog-md)** - VS Code extension with issues panel, kanban view, and editing. ([ysamlan/vscode-backlog-md](https://github.com/ysamlan/vscode-backlog-md))
428
+
429
+ ---
430
+
431
+ ## License
432
+
433
+ Backlog.md is released under the **MIT License**: do anything, just give credit. See [LICENSE](LICENSE).
package/cli.js ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+ const { constants: osConstants } = require("node:os");
5
+ const { getCandidatePackageNames, isRosettaTranslated, resolveBinaryPath } = require("./resolveBinary.cjs");
6
+
7
+ // Matches a deep path to a platform package binary that some global shims pass as an argument.
8
+ const PLATFORM_BINARY_PATH =
9
+ /node_modules[/\\]@odarino[/\\]backlog\.md-(darwin|linux|windows)-[^/\\]+[/\\]backlog(\.exe)?$/i;
10
+
11
+ function printInstallHelp() {
12
+ console.error(`Detected: ${process.platform}-${process.arch} (Node ${process.version})`);
13
+ if (process.platform === "darwin") {
14
+ const rosetta = isRosettaTranslated();
15
+ console.error(`Rosetta translation: ${rosetta ? "yes" : "no"}`);
16
+ if (rosetta) {
17
+ console.error(
18
+ "Your Node/Bun runs as x64 under Rosetta on an Apple Silicon Mac, so the wrong CPU variant may have been installed.",
19
+ );
20
+ }
21
+ console.error("To fix on macOS:");
22
+ console.error(" - Compare architectures: `node -p process.arch` vs `uname -m` (arm64 = Apple Silicon hardware).");
23
+ console.error(" - npm on Apple Silicon: `arch -arm64 npm i -g @odarino/backlog.md`");
24
+ console.error(" - Bun on Apple Silicon: `arch -arm64 bun add -g @odarino/backlog.md`");
25
+ console.error("More details: https://github.com/odarino/Backlog.md#apple-silicon-macos");
26
+ } else {
27
+ console.error("Reinstall @odarino/backlog.md so the platform package matching this architecture gets installed.");
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Spawn/exec failures that indicate a missing or wrong-architecture binary.
33
+ * macOS reports a wrong-arch Mach-O as EBADARCH (errno 86), which libuv has no
34
+ * name for, so it surfaces as errno -86 ("Unknown system error -86").
35
+ */
36
+ function isBinaryInstallError(error) {
37
+ return error?.errno === -86 || error?.code === "EBADARCH" || error?.code === "ENOEXEC" || error?.code === "ENOENT";
38
+ }
39
+
40
+ function isArchitectureSignal(signal) {
41
+ return signal === "SIGILL" || signal === "SIGTRAP";
42
+ }
43
+
44
+ function getSignalExitCode(signal) {
45
+ const signalNumber = osConstants.signals[signal];
46
+ return signalNumber ? 128 + signalNumber : 1;
47
+ }
48
+
49
+ function handleSpawnError(binaryPath, error) {
50
+ if (isBinaryInstallError(error)) {
51
+ console.error(`Cannot execute ${binaryPath} (${error.code ?? error.errno}).`);
52
+ console.error("The binary is missing or was built for a different CPU architecture.");
53
+ printInstallHelp();
54
+ } else {
55
+ console.error("Failed to start backlog:", error);
56
+ }
57
+ process.exit(1);
58
+ }
59
+
60
+ function main() {
61
+ let binaryPath;
62
+ try {
63
+ binaryPath = resolveBinaryPath();
64
+ } catch {
65
+ console.error(`Binary package not installed for ${process.platform}-${process.arch}.`);
66
+ console.error(`Tried packages: ${getCandidatePackageNames().join(", ")}`);
67
+ printInstallHelp();
68
+ process.exit(1);
69
+ }
70
+
71
+ // Clean up unexpected args some global shims pass (e.g. bun) like the binary path itself
72
+ const rawArgs = process.argv.slice(2);
73
+ const cleanedArgs = rawArgs.filter((arg) => {
74
+ if (arg === binaryPath) return false;
75
+ // Filter any accidental deep path to our platform package binary
76
+ try {
77
+ return !PLATFORM_BINARY_PATH.test(arg);
78
+ } catch {
79
+ return true;
80
+ }
81
+ });
82
+
83
+ // Spawn failures can surface as a synchronous throw (e.g. ENOEXEC) or as an 'error' event
84
+ let child;
85
+ try {
86
+ child = spawn(binaryPath, cleanedArgs, {
87
+ stdio: "inherit",
88
+ windowsHide: true,
89
+ // Internal: lets a watch end when the process that started this launcher ends.
90
+ env: { ...process.env, BACKLOG_LAUNCHER: `${process.pid}:${process.ppid}` },
91
+ });
92
+ } catch (error) {
93
+ handleSpawnError(binaryPath, error);
94
+ return;
95
+ }
96
+
97
+ child.on("exit", (code, signal) => {
98
+ if (isArchitectureSignal(signal)) {
99
+ // Typical symptom of running a binary built for the other CPU architecture
100
+ console.error(`\nbacklog crashed with ${signal} (illegal instruction): ${binaryPath}`);
101
+ console.error("The installed binary was likely built for a different CPU architecture.");
102
+ printInstallHelp();
103
+ process.exit(1);
104
+ }
105
+ if (signal) {
106
+ process.exit(getSignalExitCode(signal));
107
+ }
108
+ process.exit(code ?? 1);
109
+ });
110
+
111
+ child.on("error", (error) => handleSpawnError(binaryPath, error));
112
+ }
113
+
114
+ if (require.main === module) main();
115
+
116
+ module.exports = { PLATFORM_BINARY_PATH, getSignalExitCode, isArchitectureSignal, isBinaryInstallError };
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@odarino/backlog.md",
3
+ "version": "1.54.0",
4
+ "module": "src/cli.ts",
5
+ "files": [
6
+ "cli.js",
7
+ "resolveBinary.cjs",
8
+ "postuninstall.cjs",
9
+ "package.json",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
13
+ "bin": {
14
+ "backlog": "cli.js"
15
+ },
16
+ "optionalDependencies": {
17
+ "@odarino/backlog.md-darwin-arm64": "1.54.0",
18
+ "@odarino/backlog.md-darwin-x64": "1.54.0",
19
+ "@odarino/backlog.md-linux-arm64": "1.54.0",
20
+ "@odarino/backlog.md-linux-x64": "1.54.0",
21
+ "@odarino/backlog.md-windows-arm64": "1.54.0",
22
+ "@odarino/backlog.md-windows-x64": "1.54.0"
23
+ },
24
+ "scripts": {
25
+ "postuninstall": "node postuninstall.cjs"
26
+ },
27
+ "lint-staged": {
28
+ "package.json": [
29
+ "biome check --write --files-ignore-unknown=true"
30
+ ],
31
+ "*.json": [
32
+ "biome check --write --files-ignore-unknown=true"
33
+ ],
34
+ "src/**/*.{ts,js}": [
35
+ "biome check --write --files-ignore-unknown=true"
36
+ ]
37
+ },
38
+ "author": "Alex Gavrilescu (https://github.com/MrLesk)",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/odarino/Backlog.md"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/odarino/Backlog.md/issues"
45
+ },
46
+ "homepage": "https://github.com/odarino/Backlog.md#readme",
47
+ "keywords": [
48
+ "cli",
49
+ "markdown",
50
+ "kanban",
51
+ "task",
52
+ "project-management",
53
+ "backlog",
54
+ "agents"
55
+ ],
56
+ "license": "MIT",
57
+ "trustedDependencies": [
58
+ "@biomejs/biome",
59
+ "bun",
60
+ "node-pty"
61
+ ]
62
+ }
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { spawn } = require("node:child_process");
4
+
5
+ // Platform-specific packages to uninstall
6
+ const platformPackages = [
7
+ "@odarino/backlog.md-linux-x64",
8
+ "@odarino/backlog.md-linux-arm64",
9
+ "@odarino/backlog.md-darwin-x64",
10
+ "@odarino/backlog.md-darwin-arm64",
11
+ "@odarino/backlog.md-windows-arm64",
12
+ "@odarino/backlog.md-windows-x64",
13
+ ];
14
+
15
+ // Detect package manager
16
+ const packageManager = process.env.npm_config_user_agent?.split("/")[0] || "npm";
17
+
18
+ console.log("Cleaning up platform-specific packages...");
19
+
20
+ // Try to uninstall all platform packages
21
+ for (const pkg of platformPackages) {
22
+ const args = packageManager === "bun" ? ["remove", "-g", pkg] : ["uninstall", "-g", pkg];
23
+
24
+ const child = spawn(packageManager, args, {
25
+ stdio: "pipe", // Don't show output to avoid spam
26
+ windowsHide: true,
27
+ });
28
+
29
+ child.on("exit", (code) => {
30
+ if (code === 0) {
31
+ console.log(`✓ Cleaned up ${pkg}`);
32
+ }
33
+ // Silently ignore failures - package might not be installed
34
+ });
35
+ }
36
+
37
+ console.log("Platform package cleanup completed.");
@@ -0,0 +1,57 @@
1
+ const { execFileSync } = require("node:child_process");
2
+
3
+ function getPackageName(platform = process.platform, arch = process.arch) {
4
+ return `@odarino/backlog.md-${platform === "win32" ? "windows" : platform}-${arch}`;
5
+ }
6
+
7
+ /**
8
+ * Package names to try, in order. On macOS both darwin variants are candidates
9
+ * because the OS can run whichever one is actually installed (natively or via
10
+ * Rosetta 2). A Rosetta-translated process reports x64 while the hardware is
11
+ * arm64, so under Rosetta the arm64 (hardware) package comes first.
12
+ */
13
+ function getCandidatePackageNames(
14
+ platform = process.platform,
15
+ arch = process.arch,
16
+ rosetta = isRosettaTranslated(platform),
17
+ ) {
18
+ if (platform !== "darwin" || (arch !== "arm64" && arch !== "x64")) {
19
+ return [getPackageName(platform, arch)];
20
+ }
21
+ const primary = rosetta ? "arm64" : arch;
22
+ return [getPackageName(platform, primary), getPackageName(platform, primary === "arm64" ? "x64" : "arm64")];
23
+ }
24
+
25
+ /**
26
+ * True when the current process runs under Rosetta 2 translation on macOS.
27
+ * stdio ignores the child's stdin and stderr so restricted shells, where sysctl
28
+ * is not permitted, cannot leak "Operation not permitted" into our own stderr.
29
+ */
30
+ function isRosettaTranslated(platform = process.platform, exec = execFileSync) {
31
+ if (platform !== "darwin") return false;
32
+ try {
33
+ return (
34
+ exec("/usr/sbin/sysctl", ["-in", "sysctl.proc_translated"], {
35
+ encoding: "utf8",
36
+ stdio: ["ignore", "pipe", "ignore"],
37
+ }).trim() === "1"
38
+ );
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ function resolveBinaryPath(platform = process.platform, arch = process.arch, resolver = require.resolve) {
45
+ const binary = `backlog${platform === "win32" ? ".exe" : ""}`;
46
+ let firstError;
47
+ for (const packageName of getCandidatePackageNames(platform, arch)) {
48
+ try {
49
+ return resolver(`${packageName}/${binary}`);
50
+ } catch (error) {
51
+ firstError ??= error;
52
+ }
53
+ }
54
+ throw firstError;
55
+ }
56
+
57
+ module.exports = { getPackageName, getCandidatePackageNames, isRosettaTranslated, resolveBinaryPath };