@qelos/aidev 0.3.1 → 0.5.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.
Files changed (88) hide show
  1. package/.env.aidev.example +23 -1
  2. package/README.md +150 -17
  3. package/dist/__tests__/clickup-format.test.d.ts +2 -0
  4. package/dist/__tests__/clickup-format.test.d.ts.map +1 -0
  5. package/dist/__tests__/clickup-format.test.js +256 -0
  6. package/dist/__tests__/clickup-format.test.js.map +1 -0
  7. package/dist/__tests__/git.test.js +50 -0
  8. package/dist/__tests__/git.test.js.map +1 -1
  9. package/dist/__tests__/hooks.test.d.ts +2 -0
  10. package/dist/__tests__/hooks.test.d.ts.map +1 -0
  11. package/dist/__tests__/hooks.test.js +333 -0
  12. package/dist/__tests__/hooks.test.js.map +1 -0
  13. package/dist/__tests__/init.test.js +42 -0
  14. package/dist/__tests__/init.test.js.map +1 -1
  15. package/dist/__tests__/providers.test.js +166 -0
  16. package/dist/__tests__/providers.test.js.map +1 -1
  17. package/dist/__tests__/run.test.js +45 -0
  18. package/dist/__tests__/run.test.js.map +1 -1
  19. package/dist/cli.js +19 -4
  20. package/dist/cli.js.map +1 -1
  21. package/dist/commands/accepted.d.ts +9 -0
  22. package/dist/commands/accepted.d.ts.map +1 -0
  23. package/dist/commands/accepted.js +99 -0
  24. package/dist/commands/accepted.js.map +1 -0
  25. package/dist/commands/help.d.ts.map +1 -1
  26. package/dist/commands/help.js +7 -1
  27. package/dist/commands/help.js.map +1 -1
  28. package/dist/commands/init.d.ts +3 -0
  29. package/dist/commands/init.d.ts.map +1 -1
  30. package/dist/commands/init.js +147 -0
  31. package/dist/commands/init.js.map +1 -1
  32. package/dist/commands/run.d.ts +3 -1
  33. package/dist/commands/run.d.ts.map +1 -1
  34. package/dist/commands/run.js +128 -19
  35. package/dist/commands/run.js.map +1 -1
  36. package/dist/config.d.ts.map +1 -1
  37. package/dist/config.js +20 -1
  38. package/dist/config.js.map +1 -1
  39. package/dist/git.js +2 -2
  40. package/dist/git.js.map +1 -1
  41. package/dist/github.d.ts +5 -0
  42. package/dist/github.d.ts.map +1 -1
  43. package/dist/github.js +13 -0
  44. package/dist/github.js.map +1 -1
  45. package/dist/hooks.d.ts +106 -0
  46. package/dist/hooks.d.ts.map +1 -0
  47. package/dist/hooks.js +146 -0
  48. package/dist/hooks.js.map +1 -0
  49. package/dist/providers/base.d.ts +1 -0
  50. package/dist/providers/base.d.ts.map +1 -1
  51. package/dist/providers/clickup-format.d.ts +16 -0
  52. package/dist/providers/clickup-format.d.ts.map +1 -0
  53. package/dist/providers/clickup-format.js +255 -0
  54. package/dist/providers/clickup-format.js.map +1 -0
  55. package/dist/providers/clickup.d.ts +1 -0
  56. package/dist/providers/clickup.d.ts.map +1 -1
  57. package/dist/providers/clickup.js +23 -6
  58. package/dist/providers/clickup.js.map +1 -1
  59. package/dist/providers/index.d.ts.map +1 -1
  60. package/dist/providers/index.js +2 -1
  61. package/dist/providers/index.js.map +1 -1
  62. package/dist/providers/jira.d.ts +1 -0
  63. package/dist/providers/jira.d.ts.map +1 -1
  64. package/dist/providers/jira.js +5 -0
  65. package/dist/providers/jira.js.map +1 -1
  66. package/dist/providers/linear.d.ts +1 -0
  67. package/dist/providers/linear.d.ts.map +1 -1
  68. package/dist/providers/linear.js +5 -0
  69. package/dist/providers/linear.js.map +1 -1
  70. package/dist/providers/local.d.ts +1 -0
  71. package/dist/providers/local.d.ts.map +1 -1
  72. package/dist/providers/local.js +5 -0
  73. package/dist/providers/local.js.map +1 -1
  74. package/dist/providers/monday.d.ts +1 -0
  75. package/dist/providers/monday.d.ts.map +1 -1
  76. package/dist/providers/monday.js +5 -0
  77. package/dist/providers/monday.js.map +1 -1
  78. package/dist/providers/notion.d.ts +1 -0
  79. package/dist/providers/notion.d.ts.map +1 -1
  80. package/dist/providers/notion.js +5 -0
  81. package/dist/providers/notion.js.map +1 -1
  82. package/dist/providers/trello.d.ts +33 -0
  83. package/dist/providers/trello.d.ts.map +1 -0
  84. package/dist/providers/trello.js +269 -0
  85. package/dist/providers/trello.js.map +1 -0
  86. package/dist/types.d.ts +14 -0
  87. package/dist/types.d.ts.map +1 -1
  88. package/package.json +3 -2
@@ -3,7 +3,7 @@
3
3
  # Can also be set as a shell export (e.g. in ~/.zshrc) instead.
4
4
  # AIDEV_ENV_EXTEND=~/.aidev.global
5
5
 
6
- # PROVIDER: clickup | jira | linear | local | monday | notion
6
+ # PROVIDER: clickup | jira | linear | local | monday | notion | trello
7
7
  PROVIDER=clickup
8
8
 
9
9
  # ── ClickUp (when PROVIDER=clickup) ──────────────────────────
@@ -44,6 +44,21 @@ CLICKUP_IN_REVIEW_STATUS=review
44
44
  # NOTION_PENDING_STATUS=pending
45
45
  # NOTION_IN_REVIEW_STATUS=review
46
46
 
47
+ # ── Trello (when PROVIDER=trello) ───────────────────────────────
48
+ # Developer API key and token from https://trello.com/power-ups/admin (or trello.com/app-key)
49
+ # TRELLO_API_KEY=
50
+ # TRELLO_TOKEN=
51
+ # TRELLO_BOARD_ID= # Short board ID from the board URL
52
+ # TRELLO_LABEL= # Label name on cards to pick up; set * for all cards assigned to the token user
53
+ # List names on the board (must exist): open / pending / in progress / review workflow
54
+ # TRELLO_OPEN_LIST=To Do
55
+ # TRELLO_PENDING_LIST=Blocked
56
+ # TRELLO_IN_PROGRESS_LIST=Doing
57
+ # TRELLO_IN_REVIEW_LIST=In Review
58
+ # TRELLO_OPEN_STATUS=open
59
+ # TRELLO_PENDING_STATUS=pending
60
+ # TRELLO_IN_REVIEW_STATUS=review
61
+
47
62
  ASSIGNEE_TAG=
48
63
 
49
64
  # THINKING_TAG: tasks with this tag are analyzed and broken into sub-tasks before execution (optional)
@@ -60,3 +75,10 @@ DEV_NOTES_MODE=smart
60
75
 
61
76
  # AIDEV_COMMENT_PREFIX: custom prefix for aidev comments in task providers (default: [aidev])
62
77
  # AIDEV_COMMENT_PREFIX=[mybot]
78
+
79
+ # PR_SIGNATURE: custom signature line appended to PR body (default: "Automated PR by aidev.")
80
+ # PR_SIGNATURE=Automated PR by my-team-bot
81
+
82
+ # AIDEV_HOOKS_PATH: optional path to .ts or .js hooks module (relative to project cwd or absolute).
83
+ # aidev init creates .aidev/aidev.hooks.ts and sets this by default.
84
+ # AIDEV_HOOKS_PATH=.aidev/aidev.hooks.ts
package/README.md CHANGED
@@ -6,10 +6,10 @@
6
6
 
7
7
  **aidev** turns your tasks into merged code — automatically.
8
8
 
9
- It polls your task manager (ClickUp, Jira, Linear, Monday.com, Notion, or local markdown files), checks whether tasks are clear, runs Claude, Cursor, or Windsurf to implement them, pushes a branch, and moves the task to review. All without touching your keyboard.
9
+ It polls your task manager (ClickUp, Jira, Linear, Monday.com, Notion, Trello, or local markdown files), checks whether tasks are clear, runs Claude, Cursor, or Windsurf to implement them, pushes a branch, and moves the task to review. All without touching your keyboard.
10
10
 
11
11
  ```
12
- Task (ClickUp / Jira / Monday / Notion / local) → AI implements → git push → "in review"
12
+ Task (ClickUp / Jira / Monday / Notion / Trello / local) → AI implements → git push → "in review"
13
13
  ```
14
14
 
15
15
  ---
@@ -22,8 +22,10 @@ Task (ClickUp / Jira / Monday / Notion / local) → AI implements → git pu
22
22
  - [Concurrency lock](#concurrency-lock)
23
23
  - [Configuration](#configuration)
24
24
  - [AI agents](#ai-agents)
25
+ - [Auto-merge accepted PRs](#auto-merge-accepted-prs)
25
26
  - [Dev notes mode](#dev-notes-mode)
26
27
  - [Scheduling](#scheduling)
28
+ - [Hooks](#hooks)
27
29
  - [Logging](#logging)
28
30
  - [Providers](#providers)
29
31
  - [Contributing](#contributing)
@@ -32,7 +34,7 @@ Task (ClickUp / Jira / Monday / Notion / local) → AI implements → git pu
32
34
 
33
35
  ## How it works
34
36
 
35
- 1. **Fetch** — pulls all tasks tagged with your configured tag from ClickUp
37
+ 1. **Fetch** — pulls all tasks tagged with your configured tag from your task provider
36
38
  2. **Filter** — skips done/cancelled tasks and tasks that already have a branch
37
39
  3. **Clarify** — in `smart` mode, asks the AI if the task description is clear enough; if not, posts a question as a comment (prefixed with `[aidev]`) and marks the task `pending`
38
40
  4. **Wait** — pending tasks are re-checked on the next run; if a human replied or the trigger word is found, implementation proceeds with the conversation as context
@@ -54,7 +56,11 @@ cd my-project
54
56
  aidev init
55
57
  ```
56
58
 
57
- The wizard will ask for your ClickUp credentials, git settings, and preferred AI agents. Sensitive values (API keys) can be left blank if they are already set as environment variables in your shell.
59
+ The wizard will ask for your task provider credentials, git settings, and preferred AI agents.
60
+
61
+ > **Note:** `aidev init` currently supports **ClickUp, Jira, Linear, Local, and Monday.com**. For **Notion** and **Trello**, create `.env.aidev` manually using `.env.aidev.example` as a template (see [Configuration](#configuration)).
62
+ >
63
+ > For ClickUp, API keys can be left blank if they are already set as environment variables in your shell. For Jira, Linear, and Monday.com, the wizard requires credentials to be entered directly — to use shell env vars instead, edit `.env.aidev` after init and remove the values you want read from your environment.
58
64
 
59
65
  Once configured:
60
66
 
@@ -72,6 +78,7 @@ aidev run
72
78
  | `aidev run` | Process all open + pending-with-replies tasks |
73
79
  | `aidev run open` | Only open (non-pending) tasks |
74
80
  | `aidev run pending` | Only pending tasks — check for human replies |
81
+ | `aidev run accepted` | Auto-merge PRs for tasks in review with the accepted tag |
75
82
  | `aidev stop` | Stop any running aidev process in the current directory |
76
83
  | `aidev schedule set` | Interactive cron picker for this directory |
77
84
  | `aidev schedule set "<expr>"` | Set a specific cron expression |
@@ -167,9 +174,68 @@ CLICKUP_TAG=my-project
167
174
  | `NON_CODE_TAG` | — | Tasks with this tag run without git branching (optional) |
168
175
  | `NON_CODE_CLICKUP_TEAM_ID` | same as `CLICKUP_TEAM_ID` | Different workspace for non-code tasks (optional) |
169
176
 
170
- > **Tip:** `CLICKUP_API_KEY` and `CLICKUP_TEAM_ID` are intentionally omitted from `.env.aidev` if you leave them blank during `aidev init` — they will be read from your shell environment instead.
177
+ > **Tip:** For ClickUp, API keys and tokens are intentionally omitted from `.env.aidev` if you leave them blank during `aidev init` — they will be read from your shell environment instead. For Jira, Linear, and Monday.com, the wizard requires these values; to use shell env vars, remove the entries from `.env.aidev` after init.
178
+
179
+ > **Wildcard tag (`*`):** Set `CLICKUP_TAG=*` (or `JIRA_LABEL=*` / `LINEAR_LABEL=*` / `TRELLO_LABEL=*`) to match **all** tasks regardless of tags/labels. This is useful when the AI dev has its own dedicated user in the task provider and every task assigned to it should be processed.
180
+
181
+ ### Jira
182
+
183
+ | Variable | Default | Description |
184
+ |---|---|---|
185
+ | `JIRA_BASE_URL` | — | Jira instance URL (e.g. `https://mycompany.atlassian.net`) |
186
+ | `JIRA_EMAIL` | — | Email for Jira authentication |
187
+ | `JIRA_API_TOKEN` | — | API token — generate from [Atlassian account settings](https://id.atlassian.com/manage-profile/security/api-tokens) |
188
+ | `JIRA_PROJECT` | — | Project key (e.g. `PROJ`) |
189
+ | `JIRA_LABEL` | — | Issues with this label will be picked up (set to `*` to match all issues) |
190
+ | `JIRA_PENDING_STATUS` | `To Do` | Status name for "waiting for reply" |
191
+ | `JIRA_IN_REVIEW_STATUS` | `In Review` | Status set after implementation |
192
+ | `NON_CODE_JIRA_PROJECT` | same as `JIRA_PROJECT` | Different project for non-code tasks (optional) |
193
+
194
+ ### Linear
195
+
196
+ | Variable | Default | Description |
197
+ |---|---|---|
198
+ | `LINEAR_API_KEY` | — | Personal API key from Linear Settings → API |
199
+ | `LINEAR_TEAM_ID` | — | Team UUID from your workspace |
200
+ | `LINEAR_LABEL` | — | Issues with this label will be picked up (set to `*` to match all issues) |
201
+ | `LINEAR_PENDING_STATUS` | `Backlog` | Status name for "waiting for reply" |
202
+ | `LINEAR_IN_REVIEW_STATUS` | `In Review` | Status set after implementation |
203
+ | `NON_CODE_LINEAR_TEAM_ID` | same as `LINEAR_TEAM_ID` | Different team for non-code tasks (optional) |
204
+
205
+ ### Monday.com
206
+
207
+ | Variable | Default | Description |
208
+ |---|---|---|
209
+ | `MONDAY_API_TOKEN` | — | API token from monday.com Developer settings |
210
+ | `MONDAY_BOARD_ID` | — | Board ID (from the board URL) |
211
+ | `MONDAY_STATUS_COLUMN_ID` | `status` | Column ID for the status field |
212
+ | `MONDAY_GROUP_ID` | — | Group ID to filter items (optional) |
171
213
 
172
- > **Wildcard tag (`*`):** Set `CLICKUP_TAG=*` (or `JIRA_LABEL=*` / `LINEAR_LABEL=*`) to match **all** tasks regardless of tags/labels. This is useful when the AI dev has its own dedicated user in the task provider and every task assigned to it should be processed.
214
+ ### Notion
215
+
216
+ | Variable | Default | Description |
217
+ |---|---|---|
218
+ | `NOTION_API_KEY` | — | Integration token from Notion → Settings → My integrations |
219
+ | `NOTION_DATABASE_ID` | — | Database ID from the database URL (32-char hex) |
220
+ | `NOTION_STATUS_PROPERTY` | `Status` | Name of the status property in the database |
221
+ | `NOTION_PENDING_STATUS` | `pending` | Status value for "waiting for reply" |
222
+ | `NOTION_IN_REVIEW_STATUS` | `review` | Status value set after implementation |
223
+
224
+ ### Trello
225
+
226
+ | Variable | Default | Description |
227
+ |---|---|---|
228
+ | `TRELLO_API_KEY` | — | Developer API key from [trello.com/power-ups/admin](https://trello.com/power-ups/admin) |
229
+ | `TRELLO_TOKEN` | — | Auth token generated via Trello's token flow |
230
+ | `TRELLO_BOARD_ID` | — | Board ID from the board URL |
231
+ | `TRELLO_LABEL` | — | Label name on cards to pick up (set to `*` to match all cards assigned to the token user) |
232
+ | `TRELLO_OPEN_LIST` | `To Do` | List name for open/new cards |
233
+ | `TRELLO_PENDING_LIST` | `Blocked` | List name for "waiting for reply" |
234
+ | `TRELLO_IN_PROGRESS_LIST` | `Doing` | List name for cards being worked on |
235
+ | `TRELLO_IN_REVIEW_LIST` | `In Review` | List name for completed cards awaiting review |
236
+ | `TRELLO_OPEN_STATUS` | `open` | Semantic status mapped to the open list |
237
+ | `TRELLO_PENDING_STATUS` | `pending` | Semantic status mapped to the pending list |
238
+ | `TRELLO_IN_REVIEW_STATUS` | `review` | Semantic status mapped to the review list |
173
239
 
174
240
  ### Git & GitHub
175
241
 
@@ -188,6 +254,40 @@ CLICKUP_TAG=my-project
188
254
  | `DEV_NOTES_MODE` | `smart` | When to ask for clarification (`smart` or `always`) |
189
255
  | `AIDEV_TRIGGER_WORD` | `aidev-continue` | Comment containing this word re-triggers a skipped task |
190
256
  | `AIDEV_COMMENT_PREFIX` | `[aidev]` | Custom prefix for all aidev comments posted to task providers |
257
+ | `AIDEV_HOOKS_PATH` | — | Path to a `.ts` or `.js` module that exports hook functions (see [Hooks](#hooks)) |
258
+ | `ACCEPTED_TAG` | — | Tasks in review with this tag are auto-merged (see [Auto-merge accepted PRs](#auto-merge-accepted-prs)) |
259
+ | `DONE_STATUS` | — | Status to set after auto-merging an accepted PR (e.g. `done`) |
260
+ | `PR_SIGNATURE` | `Automated PR by aidev.` | Custom signature line appended to the PR body |
261
+
262
+ ---
263
+
264
+ ## Hooks
265
+
266
+ Set `AIDEV_HOOKS_PATH` in `.env.aidev` to a path relative to the project directory or an absolute path. `aidev init` writes `.aidev/aidev.hooks.ts` and sets `AIDEV_HOOKS_PATH=.aidev/aidev.hooks.ts` by default.
267
+
268
+ The module should export an object (or `export default`) whose properties are optional async functions. Only known hook names are used; anything else is ignored. If a hook throws, the current operation stops (for example the whole run after `beforeRun`, or conflict resolution after `beforeResolveConflicts`). If a hook returns an object, it replaces the context for that step (for example append to `context.prompt` in `beforeEachTask` and return the updated context).
269
+
270
+ **Hook names**
271
+
272
+ | Hook | When | Context notes |
273
+ |---|---|---|
274
+ | `beforeRun` / `afterRun` | Start / end of `aidev run` | `afterRun` includes `processed` and `skipped` counts |
275
+ | `beforeEachTask` / `afterEachTask` | Around each code task implementation | `prompt`, `branchName`, `task`; `afterEachTask` has `success` |
276
+ | `beforeResolveConflicts` / `afterResolveConflicts` | Merge conflict resolution with AI | `conflictFiles`, `prompt`; `afterResolveConflicts` has `resolved` |
277
+ | `beforeNonCodeTask` / `afterNonCodeTask` | Non-code tasks | `afterNonCodeTask` includes agent `output` |
278
+ | `beforeThinkingTask` / `afterThinkingTask` | Thinking-tag tasks (subtask plan) | `beforeThinkingTask` may adjust `subtasks` before steps run |
279
+
280
+ **Second argument: `vm`**
281
+
282
+ Each hook receives `(context, vm)`. The `vm` object exposes:
283
+
284
+ - `runAI(prompt)` — runs the first available configured AI agent
285
+ - `postComment(taskId, text)`, `updateStatus(taskId, status)`, `getComments(taskId)` — same family of operations as the task provider
286
+ - `log.info` / `log.warn` / `log.error` — prefixed hook logging
287
+
288
+ **TypeScript hooks**
289
+
290
+ `.ts` hook files are loaded at runtime via [jiti](https://www.npmjs.com/package/jiti) — no TypeScript compiler or toolchain needed. Just write a plain `.ts` file with the hook functions and aidev handles the rest.
191
291
 
192
292
  ---
193
293
 
@@ -291,16 +391,47 @@ If `NON_CODE_TAG` is not configured, non-code task processing is disabled entire
291
391
 
292
392
  ---
293
393
 
394
+ ## Auto-merge accepted PRs
395
+
396
+ When a task has been reviewed and is ready to merge, tag it with your configured `ACCEPTED_TAG`. On the next run, aidev will automatically merge the PR via the GitHub CLI (`gh`), update the task status, and sync your local main branch.
397
+
398
+ This feature is **optional** — it only activates when both `ACCEPTED_TAG` is configured and the `gh` CLI is installed and authenticated.
399
+
400
+ ```bash
401
+ # In .env.aidev
402
+ ACCEPTED_TAG=accepted
403
+ DONE_STATUS=done
404
+ ```
405
+
406
+ **How it works:**
407
+
408
+ 1. Finds all tasks in your "in review" status that have the accepted tag
409
+ 2. For each task: merges the PR with squash and deletes the remote branch (`gh pr merge --squash --delete-branch`)
410
+ 3. Updates the task status to `DONE_STATUS` (if configured)
411
+ 4. Checks out the base branch and pulls the latest changes
412
+
413
+ **Run it manually:**
414
+
415
+ ```bash
416
+ aidev run accepted
417
+ ```
418
+
419
+ **Automatic mode:** When `ACCEPTED_TAG` is set and `gh` is available, accepted PRs are also auto-merged at the end of every `aidev run`.
420
+
421
+ > **Prerequisites:** The [GitHub CLI](https://cli.github.com/) must be installed and authenticated (`gh auth login`). `aidev init` will prompt you for these settings if it detects `gh` on your PATH.
422
+
423
+ ---
424
+
294
425
  ## Dev notes mode
295
426
 
296
- Controls when aidev asks ClickUp for clarification before implementing.
427
+ Controls when aidev asks the task provider for clarification before implementing.
297
428
 
298
429
  | Mode | Behaviour |
299
430
  |---|---|
300
431
  | `smart` | Asks the AI whether the task description is clear enough. Only posts a clarification question if it's ambiguous. |
301
432
  | `always` | Always posts "any dev notes?" before implementing every task. |
302
433
 
303
- When a question is posted, the task is moved to `CLICKUP_PENDING_STATUS`. On the next run, aidev checks whether a human has replied and, if so, includes the reply as context for the AI.
434
+ When a question is posted, the task is moved to the configured pending status (e.g. `CLICKUP_PENDING_STATUS`, `JIRA_PENDING_STATUS`, etc.). On the next run, aidev checks whether a human has replied and, if so, includes the reply as context for the AI.
304
435
 
305
436
  ---
306
437
 
@@ -364,15 +495,17 @@ ANSI colour codes are stripped so the file stays readable in any editor or `tail
364
495
 
365
496
  ## Providers
366
497
 
367
- | Provider | Status |
368
- |---|---|
369
- | ClickUp | ✅ Implemented |
370
- | Jira | ✅ Implemented |
371
- | Linear | ✅ Implemented |
372
- | Monday.com | ✅ Implemented |
373
- | Local | ✅ Implemented |
374
- | Notion | ✅ Implemented |
375
- | Trello | 🔜 Stub contributions welcome |
498
+ | Provider | Status | `aidev init` support |
499
+ |---|---|---|
500
+ | ClickUp | ✅ Implemented | ✅ Interactive wizard |
501
+ | Jira | ✅ Implemented | ✅ Interactive wizard |
502
+ | Linear | ✅ Implemented | ✅ Interactive wizard |
503
+ | Monday.com | ✅ Implemented | ✅ Interactive wizard |
504
+ | Local | ✅ Implemented | ✅ Interactive wizard |
505
+ | Notion | ✅ Implemented | Manual `.env.aidev` config |
506
+ | Trello | Implemented | Manual `.env.aidev` config |
507
+
508
+ > **Notion & Trello:** These providers are fully functional but not yet included in the `aidev init` wizard. To use them, set `PROVIDER=notion` or `PROVIDER=trello` in `.env.aidev` and fill in the required variables from the [Configuration](#configuration) section above.
376
509
 
377
510
  The `TaskProvider` interface makes it straightforward to add new providers. See [CONTRIBUTING.md](./CONTRIBUTING.md).
378
511
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=clickup-format.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickup-format.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/clickup-format.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,256 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_test_1 = require("node:test");
7
+ const strict_1 = __importDefault(require("node:assert/strict"));
8
+ const clickup_format_1 = require("../providers/clickup-format");
9
+ (0, node_test_1.describe)('clickupBlocksToMarkdown', () => {
10
+ (0, node_test_1.it)('converts plain text blocks', () => {
11
+ const blocks = [
12
+ { text: 'Hello world' },
13
+ { text: '\n' },
14
+ ];
15
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), 'Hello world');
16
+ });
17
+ (0, node_test_1.it)('converts bold text', () => {
18
+ const blocks = [
19
+ { text: 'This is ', attributes: {} },
20
+ { text: 'bold', attributes: { bold: true } },
21
+ { text: ' text\n' },
22
+ ];
23
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), 'This is **bold** text');
24
+ });
25
+ (0, node_test_1.it)('converts italic text', () => {
26
+ const blocks = [
27
+ { text: 'This is ', attributes: {} },
28
+ { text: 'italic', attributes: { italic: true } },
29
+ { text: '\n' },
30
+ ];
31
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), 'This is *italic*');
32
+ });
33
+ (0, node_test_1.it)('converts inline code', () => {
34
+ const blocks = [
35
+ { text: 'Use ' },
36
+ { text: 'npm install', attributes: { code: true } },
37
+ { text: '\n' },
38
+ ];
39
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), 'Use `npm install`');
40
+ });
41
+ (0, node_test_1.it)('converts links', () => {
42
+ const blocks = [
43
+ { text: 'Click ' },
44
+ { text: 'here', attributes: { link: 'https://example.com' } },
45
+ { text: '\n' },
46
+ ];
47
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), 'Click [here](https://example.com)');
48
+ });
49
+ (0, node_test_1.it)('converts bullet list', () => {
50
+ const blocks = [
51
+ { text: 'First item' },
52
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
53
+ { text: 'Second item' },
54
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
55
+ ];
56
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '- First item\n- Second item');
57
+ });
58
+ (0, node_test_1.it)('converts ordered list', () => {
59
+ const blocks = [
60
+ { text: 'First' },
61
+ { text: '\n', attributes: { list: { list: 'ordered' } } },
62
+ { text: 'Second' },
63
+ { text: '\n', attributes: { list: { list: 'ordered' } } },
64
+ ];
65
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '1. First\n2. Second');
66
+ });
67
+ (0, node_test_1.it)('converts checklist', () => {
68
+ const blocks = [
69
+ { text: 'Done task' },
70
+ { text: '\n', attributes: { list: { list: 'checked' } } },
71
+ { text: 'Pending task' },
72
+ { text: '\n', attributes: { list: { list: 'unchecked' } } },
73
+ ];
74
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '- [x] Done task\n- [ ] Pending task');
75
+ });
76
+ (0, node_test_1.it)('converts code block', () => {
77
+ const blocks = [
78
+ { text: 'const x = 1;' },
79
+ { text: '\n', attributes: { 'code-block': 'javascript' } },
80
+ { text: 'const y = 2;' },
81
+ { text: '\n', attributes: { 'code-block': 'javascript' } },
82
+ ];
83
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '```\nconst x = 1;\nconst y = 2;\n```');
84
+ });
85
+ (0, node_test_1.it)('handles code-block with nested object format', () => {
86
+ const blocks = [
87
+ { text: 'hello()' },
88
+ { text: '\n', attributes: { 'code-block': { 'code-block': 'plain' } } },
89
+ ];
90
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '```\nhello()\n```');
91
+ });
92
+ (0, node_test_1.it)('handles mixed content', () => {
93
+ const blocks = [
94
+ { text: 'Title', attributes: { bold: true } },
95
+ { text: '\n' },
96
+ { text: 'Item A' },
97
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
98
+ { text: 'Item B' },
99
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
100
+ ];
101
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '**Title**\n- Item A\n- Item B');
102
+ });
103
+ (0, node_test_1.it)('returns empty string for empty/null input', () => {
104
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)([]), '');
105
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(null), '');
106
+ });
107
+ (0, node_test_1.it)('handles list attribute as plain string', () => {
108
+ const blocks = [
109
+ { text: 'item' },
110
+ { text: '\n', attributes: { list: 'bullet' } },
111
+ ];
112
+ strict_1.default.equal((0, clickup_format_1.clickupBlocksToMarkdown)(blocks), '- item');
113
+ });
114
+ });
115
+ (0, node_test_1.describe)('markdownToClickupBlocks', () => {
116
+ (0, node_test_1.it)('converts plain text', () => {
117
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('Hello world');
118
+ strict_1.default.deepEqual(blocks, [
119
+ { text: 'Hello world' },
120
+ { text: '\n' },
121
+ ]);
122
+ });
123
+ (0, node_test_1.it)('converts bold text', () => {
124
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('This is **bold** text');
125
+ strict_1.default.deepEqual(blocks, [
126
+ { text: 'This is ' },
127
+ { text: 'bold', attributes: { bold: true } },
128
+ { text: ' text' },
129
+ { text: '\n' },
130
+ ]);
131
+ });
132
+ (0, node_test_1.it)('converts italic text', () => {
133
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('This is *italic* text');
134
+ strict_1.default.deepEqual(blocks, [
135
+ { text: 'This is ' },
136
+ { text: 'italic', attributes: { italic: true } },
137
+ { text: ' text' },
138
+ { text: '\n' },
139
+ ]);
140
+ });
141
+ (0, node_test_1.it)('converts inline code', () => {
142
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('Use `npm install` now');
143
+ strict_1.default.deepEqual(blocks, [
144
+ { text: 'Use ' },
145
+ { text: 'npm install', attributes: { code: true } },
146
+ { text: ' now' },
147
+ { text: '\n' },
148
+ ]);
149
+ });
150
+ (0, node_test_1.it)('converts links', () => {
151
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('Click [here](https://example.com)');
152
+ strict_1.default.deepEqual(blocks, [
153
+ { text: 'Click ' },
154
+ { text: 'here', attributes: { link: 'https://example.com' } },
155
+ { text: '\n' },
156
+ ]);
157
+ });
158
+ (0, node_test_1.it)('converts bullet list', () => {
159
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('- First\n- Second');
160
+ strict_1.default.deepEqual(blocks, [
161
+ { text: 'First' },
162
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
163
+ { text: 'Second' },
164
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
165
+ ]);
166
+ });
167
+ (0, node_test_1.it)('converts ordered list', () => {
168
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('1. First\n2. Second');
169
+ strict_1.default.deepEqual(blocks, [
170
+ { text: 'First' },
171
+ { text: '\n', attributes: { list: { list: 'ordered' } } },
172
+ { text: 'Second' },
173
+ { text: '\n', attributes: { list: { list: 'ordered' } } },
174
+ ]);
175
+ });
176
+ (0, node_test_1.it)('converts checklist', () => {
177
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('- [x] Done\n- [ ] Todo');
178
+ strict_1.default.deepEqual(blocks, [
179
+ { text: 'Done' },
180
+ { text: '\n', attributes: { list: { list: 'checked' } } },
181
+ { text: 'Todo' },
182
+ { text: '\n', attributes: { list: { list: 'unchecked' } } },
183
+ ]);
184
+ });
185
+ (0, node_test_1.it)('converts code block', () => {
186
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('```js\nconst x = 1;\n```');
187
+ strict_1.default.deepEqual(blocks, [
188
+ { text: 'const x = 1;' },
189
+ { text: '\n', attributes: { 'code-block': { 'code-block': 'js' } } },
190
+ ]);
191
+ });
192
+ (0, node_test_1.it)('converts headings to bold', () => {
193
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('## My Heading');
194
+ strict_1.default.deepEqual(blocks, [
195
+ { text: 'My Heading', attributes: { bold: true } },
196
+ { text: '\n' },
197
+ ]);
198
+ });
199
+ (0, node_test_1.it)('handles empty lines as paragraph breaks', () => {
200
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('Line 1\n\nLine 2');
201
+ strict_1.default.deepEqual(blocks, [
202
+ { text: 'Line 1' },
203
+ { text: '\n' },
204
+ { text: '\n' },
205
+ { text: 'Line 2' },
206
+ { text: '\n' },
207
+ ]);
208
+ });
209
+ (0, node_test_1.it)('handles inline formatting inside list items', () => {
210
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)('- **bold** item');
211
+ strict_1.default.deepEqual(blocks, [
212
+ { text: 'bold', attributes: { bold: true } },
213
+ { text: ' item' },
214
+ { text: '\n', attributes: { list: { list: 'bullet' } } },
215
+ ]);
216
+ });
217
+ });
218
+ (0, node_test_1.describe)('round-trip conversion', () => {
219
+ (0, node_test_1.it)('preserves plain text through round-trip', () => {
220
+ const md = 'Hello world';
221
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)(md);
222
+ const result = (0, clickup_format_1.clickupBlocksToMarkdown)(blocks);
223
+ strict_1.default.equal(result, md);
224
+ });
225
+ (0, node_test_1.it)('preserves bullet list through round-trip', () => {
226
+ const md = '- Item A\n- Item B\n- Item C';
227
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)(md);
228
+ const result = (0, clickup_format_1.clickupBlocksToMarkdown)(blocks);
229
+ strict_1.default.equal(result, md);
230
+ });
231
+ (0, node_test_1.it)('preserves ordered list through round-trip', () => {
232
+ const md = '1. First\n2. Second\n3. Third';
233
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)(md);
234
+ const result = (0, clickup_format_1.clickupBlocksToMarkdown)(blocks);
235
+ strict_1.default.equal(result, md);
236
+ });
237
+ (0, node_test_1.it)('preserves bold and italic through round-trip', () => {
238
+ const md = 'This is **bold** and *italic* text';
239
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)(md);
240
+ const result = (0, clickup_format_1.clickupBlocksToMarkdown)(blocks);
241
+ strict_1.default.equal(result, md);
242
+ });
243
+ (0, node_test_1.it)('preserves inline code through round-trip', () => {
244
+ const md = 'Run `npm test` now';
245
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)(md);
246
+ const result = (0, clickup_format_1.clickupBlocksToMarkdown)(blocks);
247
+ strict_1.default.equal(result, md);
248
+ });
249
+ (0, node_test_1.it)('preserves links through round-trip', () => {
250
+ const md = 'Visit [docs](https://docs.example.com) for info';
251
+ const blocks = (0, clickup_format_1.markdownToClickupBlocks)(md);
252
+ const result = (0, clickup_format_1.clickupBlocksToMarkdown)(blocks);
253
+ strict_1.default.equal(result, md);
254
+ });
255
+ });
256
+ //# sourceMappingURL=clickup-format.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clickup-format.test.js","sourceRoot":"","sources":["../../src/__tests__/clickup-format.test.ts"],"names":[],"mappings":"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"}
@@ -233,6 +233,56 @@ function initRepo(dir) {
233
233
  (0, node_test_1.it)('fails when the remote base branch does not exist', () => {
234
234
  strict_1.default.equal((0, git_1.createBranchFromRemote)('origin', 'nonexistent', 'task000/bad'), false);
235
235
  });
236
+ (0, node_test_1.it)('falls back to checking out existing branch when checkout -b fails (pending task follow-up)', () => {
237
+ // Simulate the pending-task scenario: branch already exists on the remote
238
+ // because a previous run created it. A follow-up comment triggers another run
239
+ // which calls createBranchFromRemote again — checkout -b fails because
240
+ // the branch already exists, so it should fallback to fetchAndCheckoutBranch.
241
+ gitCmd(['checkout', '-b', 'task123/existing-branch'], tmpDir);
242
+ fs.writeFileSync(path.join(tmpDir, 'work.txt'), 'previous work');
243
+ gitCmd(['add', '.'], tmpDir);
244
+ gitCmd(['commit', '-m', 'work on existing branch'], tmpDir);
245
+ gitCmd(['push', 'origin', 'task123/existing-branch'], tmpDir);
246
+ // Switch back to main so we're not already on the target branch
247
+ gitCmd(['checkout', 'main'], tmpDir);
248
+ // Now createBranchFromRemote should fail on checkout -b (branch exists)
249
+ // but succeed by falling back to checking out the existing branch
250
+ strict_1.default.equal((0, git_1.createBranchFromRemote)('origin', 'main', 'task123/existing-branch'), true);
251
+ strict_1.default.equal((0, git_1.getCurrentBranch)(), 'task123/existing-branch');
252
+ // Should have the file from the existing branch
253
+ strict_1.default.ok(fs.existsSync(path.join(tmpDir, 'work.txt')));
254
+ });
255
+ (0, node_test_1.it)('falls back and picks up new remote commits on existing branch', () => {
256
+ // Create the branch, push it, then switch away — keep local branch alive
257
+ // so checkout -b will fail and trigger the fallback path
258
+ gitCmd(['checkout', '-b', 'task456/followup'], tmpDir);
259
+ fs.writeFileSync(path.join(tmpDir, 'v1.txt'), 'first version');
260
+ gitCmd(['add', '.'], tmpDir);
261
+ gitCmd(['commit', '-m', 'v1'], tmpDir);
262
+ gitCmd(['push', 'origin', 'task456/followup'], tmpDir);
263
+ gitCmd(['checkout', 'main'], tmpDir);
264
+ // Push a new commit to that branch from a separate clone
265
+ const cloneDir = fs.mkdtempSync(path.join(os.tmpdir(), 'aidev-clone-'));
266
+ try {
267
+ gitCmd(['clone', bareDir, cloneDir], cloneDir);
268
+ gitCmd(['config', 'user.email', 'other@test.com'], cloneDir);
269
+ gitCmd(['config', 'user.name', 'Other'], cloneDir);
270
+ gitCmd(['checkout', 'task456/followup'], cloneDir);
271
+ fs.writeFileSync(path.join(cloneDir, 'v2.txt'), 'second version');
272
+ gitCmd(['add', '.'], cloneDir);
273
+ gitCmd(['commit', '-m', 'v2'], cloneDir);
274
+ gitCmd(['push', 'origin', 'task456/followup'], cloneDir);
275
+ }
276
+ finally {
277
+ fs.rmSync(cloneDir, { recursive: true, force: true });
278
+ }
279
+ // checkout -b fails (branch exists locally), fallback checks out + pulls
280
+ strict_1.default.equal((0, git_1.createBranchFromRemote)('origin', 'main', 'task456/followup'), true);
281
+ strict_1.default.equal((0, git_1.getCurrentBranch)(), 'task456/followup');
282
+ // Should have both the original and the new remote commit's files
283
+ strict_1.default.ok(fs.existsSync(path.join(tmpDir, 'v1.txt')));
284
+ strict_1.default.ok(fs.existsSync(path.join(tmpDir, 'v2.txt')));
285
+ });
236
286
  });
237
287
  (0, node_test_1.describe)('commit with expectedBranch (integration)', () => {
238
288
  let tmpDir;