@vexdo/cli 0.1.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 (51) hide show
  1. package/.eslintrc.json +23 -0
  2. package/.github/workflows/ci.yml +84 -0
  3. package/.idea/copilot.data.migration.ask2agent.xml +6 -0
  4. package/.idea/go.imports.xml +11 -0
  5. package/.idea/misc.xml +6 -0
  6. package/.idea/modules.xml +8 -0
  7. package/.idea/vcs.xml +7 -0
  8. package/.idea/vexdo-cli.iml +9 -0
  9. package/.prettierrc +5 -0
  10. package/CLAUDE.md +93 -0
  11. package/CONTRIBUTING.md +62 -0
  12. package/LICENSE +21 -0
  13. package/README.md +206 -0
  14. package/bin/vexdo.js +2 -0
  15. package/package.json +35 -0
  16. package/src/commands/abort.ts +66 -0
  17. package/src/commands/fix.ts +106 -0
  18. package/src/commands/init.ts +142 -0
  19. package/src/commands/logs.ts +74 -0
  20. package/src/commands/review.ts +107 -0
  21. package/src/commands/start.ts +197 -0
  22. package/src/commands/status.ts +52 -0
  23. package/src/commands/submit.ts +38 -0
  24. package/src/index.ts +42 -0
  25. package/src/lib/claude.ts +259 -0
  26. package/src/lib/codex.ts +96 -0
  27. package/src/lib/config.ts +157 -0
  28. package/src/lib/gh.ts +78 -0
  29. package/src/lib/git.ts +119 -0
  30. package/src/lib/logger.ts +147 -0
  31. package/src/lib/requirements.ts +18 -0
  32. package/src/lib/review-loop.ts +154 -0
  33. package/src/lib/state.ts +121 -0
  34. package/src/lib/submit-task.ts +43 -0
  35. package/src/lib/tasks.ts +94 -0
  36. package/src/prompts/arbiter.ts +21 -0
  37. package/src/prompts/reviewer.ts +20 -0
  38. package/src/types/index.ts +96 -0
  39. package/test/config.test.ts +124 -0
  40. package/test/state.test.ts +147 -0
  41. package/test/unit/claude.test.ts +117 -0
  42. package/test/unit/codex.test.ts +67 -0
  43. package/test/unit/gh.test.ts +49 -0
  44. package/test/unit/git.test.ts +120 -0
  45. package/test/unit/review-loop.test.ts +198 -0
  46. package/tests/integration/review.test.ts +137 -0
  47. package/tests/integration/start.test.ts +220 -0
  48. package/tests/unit/init.test.ts +91 -0
  49. package/tsconfig.json +15 -0
  50. package/tsup.config.ts +8 -0
  51. package/vitest.config.ts +7 -0
package/.eslintrc.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "root": true,
3
+ "env": {
4
+ "node": true,
5
+ "es2022": true
6
+ },
7
+ "parser": "@typescript-eslint/parser",
8
+ "parserOptions": {
9
+ "project": "./tsconfig.json",
10
+ "sourceType": "module"
11
+ },
12
+ "plugins": ["@typescript-eslint"],
13
+ "extends": [
14
+ "eslint:recommended",
15
+ "plugin:@typescript-eslint/strict-type-checked",
16
+ "plugin:@typescript-eslint/stylistic-type-checked",
17
+ "prettier"
18
+ ],
19
+ "rules": {
20
+ "@typescript-eslint/consistent-type-imports": "error"
21
+ },
22
+ "ignorePatterns": ["dist", "bin/*.js"]
23
+ }
@@ -0,0 +1,84 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - '**'
7
+ tags:
8
+ - 'v*'
9
+ pull_request:
10
+
11
+ jobs:
12
+ lint-typecheck:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-node@v4
17
+ with:
18
+ node-version: 18
19
+ - uses: actions/cache@v4
20
+ with:
21
+ path: ~/.npm
22
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
23
+ restore-keys: |
24
+ npm-${{ runner.os }}-
25
+ - run: npm ci
26
+ - run: npm run typecheck
27
+ - run: npm run lint
28
+
29
+ test:
30
+ runs-on: ubuntu-latest
31
+ needs: lint-typecheck
32
+ steps:
33
+ - uses: actions/checkout@v4
34
+ - uses: actions/setup-node@v4
35
+ with:
36
+ node-version: 18
37
+ - uses: actions/cache@v4
38
+ with:
39
+ path: ~/.npm
40
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
41
+ restore-keys: |
42
+ npm-${{ runner.os }}-
43
+ - run: npm ci
44
+ - run: npm run test:coverage
45
+
46
+ build:
47
+ runs-on: ubuntu-latest
48
+ needs: test
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: actions/setup-node@v4
52
+ with:
53
+ node-version: 18
54
+ - uses: actions/cache@v4
55
+ with:
56
+ path: ~/.npm
57
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
58
+ restore-keys: |
59
+ npm-${{ runner.os }}-
60
+ - run: npm ci
61
+ - run: npm run build
62
+ - run: test -n "$(find dist -mindepth 1 -maxdepth 1 -print -quit)"
63
+
64
+ release:
65
+ if: startsWith(github.ref, 'refs/tags/v')
66
+ runs-on: ubuntu-latest
67
+ needs: [lint-typecheck, test, build]
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+ - uses: actions/setup-node@v4
71
+ with:
72
+ node-version: 18
73
+ registry-url: https://registry.npmjs.org
74
+ - uses: actions/cache@v4
75
+ with:
76
+ path: ~/.npm
77
+ key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
78
+ restore-keys: |
79
+ npm-${{ runner.os }}-
80
+ - run: npm ci
81
+ - run: npm run build
82
+ - run: npm publish
83
+ env:
84
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Ask2AgentMigrationStateService">
4
+ <option name="migrationStatus" value="COMPLETED" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,11 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="GoImports">
4
+ <option name="excludedPackages">
5
+ <array>
6
+ <option value="github.com/pkg/errors" />
7
+ <option value="golang.org/x/net/context" />
8
+ </array>
9
+ </option>
10
+ </component>
11
+ </project>
package/.idea/misc.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="openjdk-24" project-jdk-type="JavaSDK">
4
+ <output url="file://$PROJECT_DIR$/out" />
5
+ </component>
6
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/.idea/vexdo-cli.iml" filepath="$PROJECT_DIR$/.idea/vexdo-cli.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,7 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
6
+ </component>
7
+ </project>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="JAVA_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+ <exclude-output />
5
+ <content url="file://$MODULE_DIR$" />
6
+ <orderEntry type="inheritedJdk" />
7
+ <orderEntry type="sourceFolder" forTests="false" />
8
+ </component>
9
+ </module>
package/.prettierrc ADDED
@@ -0,0 +1,5 @@
1
+ {
2
+ "singleQuote": true,
3
+ "trailingComma": "all",
4
+ "printWidth": 100
5
+ }
package/CLAUDE.md ADDED
@@ -0,0 +1,93 @@
1
+ # CLAUDE.md
2
+
3
+ ## 1) Project overview
4
+
5
+ `vexdo` is a task orchestrator CLI for multi-service repositories. The core flow is: load task + config, run Codex for each step, run Claude reviewer + arbiter loop, then submit PRs or escalate.
6
+
7
+ ## 2) Architecture
8
+
9
+ Dependency direction should stay:
10
+
11
+ ```text
12
+ types ← lib ← commands ← index.ts
13
+ ```
14
+
15
+ Key invariants:
16
+ - `commands/` may exit process; `lib/` should generally return errors.
17
+ - `lib/` is reusable orchestration/integration logic.
18
+ - `types/` stays free of runtime dependencies.
19
+ - Reviewer and arbiter must run with isolated contexts/data.
20
+
21
+ ## 3) Key files and roles
22
+
23
+ ```text
24
+ src/
25
+ index.ts # commander bootstrap, global flags, command wiring
26
+ commands/
27
+ init.ts # interactive bootstrap (`vexdo init`)
28
+ start.ts # primary task execution flow
29
+ review.ts # rerun review loop on active step
30
+ fix.ts # feed corrective prompt to Codex then review
31
+ submit.ts # PR creation and state completion
32
+ abort.ts # cancel task, preserve branches
33
+ status.ts # print active state
34
+ logs.ts # print iteration logs
35
+ lib/
36
+ config.ts # .vexdo.yml discovery + validation
37
+ tasks.ts # task loading/validation and lane moves
38
+ state.ts # active state persistence
39
+ review-loop.ts # reviewer/arbiter loop control
40
+ claude.ts # Anthropic SDK wrapper
41
+ codex.ts # codex CLI wrapper
42
+ gh.ts # gh CLI wrapper for PRs
43
+ git.ts # git operations
44
+ logger.ts # output formatting
45
+ requirements.ts # env/runtime checks
46
+ prompts/
47
+ reviewer.ts # reviewer prompt construction
48
+ arbiter.ts # arbiter prompt construction
49
+ types/index.ts # shared type contracts
50
+ ```
51
+
52
+ ## 4) Common tasks
53
+
54
+ ### Add a command
55
+ 1. Add `src/commands/<name>.ts` with `register<Name>Command`.
56
+ 2. Keep parsing and CLI concerns in command file.
57
+ 3. Put reusable logic in `lib/`.
58
+ 4. Register in `src/index.ts`.
59
+ 5. Add tests and README docs.
60
+
61
+ ### Add a lib function
62
+ 1. Add function to the nearest `lib/*` module.
63
+ 2. Keep signature typed and avoid `any`.
64
+ 3. Return errors; avoid process termination.
65
+ 4. Add focused unit tests.
66
+
67
+ ### Add a test
68
+ 1. Unit tests in `test/unit` or `tests/unit` for isolated behavior.
69
+ 2. Integration tests in `test/integration` or `tests/integration` for flow.
70
+ 3. Use `vi.mock` for `child_process`, SDKs, and external CLIs.
71
+
72
+ ## 5) Constraints to always enforce
73
+
74
+ - No `any` in new code.
75
+ - Keep ESM `.js` import specifiers in TS source.
76
+ - No `process.exit` inside `lib/` modules.
77
+ - Do not add `chalk`; use `picocolors` for output styling.
78
+ - Reviewer and arbiter contexts must remain isolated.
79
+
80
+ ## 6) Test conventions
81
+
82
+ - Prefer mocking at module boundary.
83
+ - `child_process` calls should be mocked with `vi.mock('node:child_process', ...)`.
84
+ - Anthropic SDK behavior should be mocked through `lib/claude.ts` dependencies.
85
+ - Use temp directories for filesystem side effects.
86
+
87
+ ## 7) What not to do
88
+
89
+ - Do not embed orchestration logic directly in `index.ts`.
90
+ - Do not tightly couple command handlers to specific prompt text.
91
+ - Do not bypass task/config validation.
92
+ - Do not mutate state without persisting through `state.ts` helpers.
93
+ - Do not mix reviewer and arbiter outputs in a single decision context.
@@ -0,0 +1,62 @@
1
+ # Contributing to vexdo
2
+
3
+ ## 1) Development setup
4
+
5
+ ```bash
6
+ git clone <repo-url>
7
+ cd vexdo-cli
8
+ npm install
9
+ npm link
10
+ npm test
11
+ ```
12
+
13
+ Useful checks:
14
+ - `npm run typecheck`
15
+ - `npm run lint`
16
+ - `npm run build`
17
+
18
+ ## 2) Project structure
19
+
20
+ ```text
21
+ src/
22
+ index.ts # CLI entrypoint and command registration
23
+ commands/ # User-facing command handlers
24
+ lib/ # Core orchestration, integrations, and helpers
25
+ prompts/ # Claude reviewer/arbiter prompt templates
26
+ types/ # Shared TypeScript types
27
+ test/ + tests/ # Unit and integration tests
28
+ ```
29
+
30
+ ## 3) Adding a new command
31
+
32
+ 1. Create `src/commands/<name>.ts`.
33
+ 2. Export `register<Name>Command(program: Command)`.
34
+ 3. Implement `run<Name>` function and keep side effects inside command layer.
35
+ 4. Add registration in `src/index.ts`.
36
+ 5. Add unit tests for parsing/behavior and integration tests if command touches git/fs/process.
37
+ 6. Update README command docs.
38
+
39
+ ## 4) Testing conventions
40
+
41
+ - **Unit tests**: isolated logic, heavy mocking (`child_process`, SDK clients, filesystem helpers).
42
+ - **Integration tests**: flow-level behavior and state transitions.
43
+ - Prefer deterministic fixtures and temporary directories.
44
+ - External dependencies (Anthropic, gh, codex) must be mocked in unit tests.
45
+
46
+ ## 5) Commit conventions
47
+
48
+ - Follow Conventional Commits, e.g.:
49
+ - `feat: add init command`
50
+ - `fix: prevent duplicate gitignore entry`
51
+ - `docs: expand README`
52
+ - PRs should include:
53
+ - clear summary
54
+ - tests run
55
+ - any breaking changes
56
+
57
+ ## 6) Release process
58
+
59
+ 1. Bump version and update changelog/release notes.
60
+ 2. Push tag `vX.Y.Z`.
61
+ 3. GitHub Actions CI runs lint/typecheck/test/build.
62
+ 4. Release job publishes to npm using `NPM_TOKEN`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vexdo
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,206 @@
1
+ # vexdo
2
+
3
+ Automated implementation + review loop for multi-service tasks, powered by Codex and Claude.
4
+
5
+ ![CI](https://github.com/vexdo/vexdo-cli/actions/workflows/ci.yml/badge.svg)
6
+ ![npm](https://img.shields.io/npm/v/vexdo)
7
+ ![license](https://img.shields.io/npm/l/vexdo)
8
+ ![node](https://img.shields.io/node/v/vexdo)
9
+
10
+ ## 1) What is vexdo
11
+
12
+ `vexdo` is a CLI that turns a task spec into a controlled execution pipeline across one or more services. It applies changes with Codex, reviews with Claude, loops until quality gates are met, and then opens PRs (or escalates when needed).
13
+
14
+ Real-world example: you need to add a new billing field in API, web, and worker repos. Instead of running separate ad-hoc sessions, you define one `task.yml` with ordered steps and let `vexdo` orchestrate execution + review with traceable state.
15
+
16
+ ## 2) How it works
17
+
18
+ ```text
19
+ task.yml
20
+
21
+ vexdo start
22
+
23
+ codex exec
24
+
25
+ git diff
26
+
27
+ claude reviewer
28
+
29
+ claude arbiter
30
+
31
+ fix loop (max N)
32
+
33
+ PR or escalate
34
+ ```
35
+
36
+ ## 3) Requirements
37
+
38
+ - Node.js >= 18
39
+ - `ANTHROPIC_API_KEY` environment variable
40
+ - Codex CLI installed globally:
41
+ - `npm install -g @openai/codex`
42
+ - GitHub CLI installed:
43
+ - https://cli.github.com
44
+
45
+ ## 4) Quick start (5 minutes)
46
+
47
+ ```bash
48
+ npm install -g vexdo
49
+ cd my-project
50
+ vexdo init
51
+ # create tasks/backlog/my-task.yml
52
+ vexdo start tasks/backlog/my-task.yml
53
+ ```
54
+
55
+ ## 5) Commands
56
+
57
+ | Command | Description |
58
+ |---|---|
59
+ | `vexdo init` | Initialize `.vexdo.yml`, folders, and `.gitignore` entry. |
60
+ | `vexdo start <task-file> [--resume]` | Start a task and run implementation/review orchestration. |
61
+ | `vexdo review` | Re-run review loop for the current step. |
62
+ | `vexdo fix <feedback>` | Send targeted feedback to Codex, then review again. |
63
+ | `vexdo submit` | Create PRs for active task branches. |
64
+ | `vexdo status` | Show current task state. |
65
+ | `vexdo logs [task-id] [--full]` | Inspect iteration logs. |
66
+ | `vexdo abort [--force]` | Abort active task and move task file back to backlog. |
67
+
68
+ ### Global flags
69
+
70
+ - `--verbose`: Print debug logs.
71
+ - `--dry-run`: Show actions without making changes.
72
+
73
+ ### Command details
74
+
75
+ #### `vexdo init`
76
+ Interactive bootstrap wizard:
77
+ - asks service names/paths
78
+ - asks review and model defaults
79
+ - writes `.vexdo.yml`
80
+ - creates `tasks/*` lanes and `.vexdo/logs/`
81
+ - adds `.vexdo/` to `.gitignore` once
82
+
83
+ #### `vexdo start <task-file>`
84
+ Flags:
85
+ - `--resume`: resume from existing state if present
86
+
87
+ Behavior:
88
+ - validates config and task
89
+ - creates/checks service branches
90
+ - runs Codex then review loop per step
91
+ - moves task files across lanes (`backlog → in_progress → review/done/blocked`)
92
+
93
+ #### `vexdo review`
94
+ Runs review + arbiter flow against current step without restarting full task.
95
+
96
+ #### `vexdo fix <feedback>`
97
+ Runs Codex with your corrective feedback, then re-enters review loop.
98
+
99
+ #### `vexdo submit`
100
+ Creates PRs for each service branch in the active task and marks task as done.
101
+
102
+ #### `vexdo status`
103
+ Prints a concise summary of active task id/title/step statuses.
104
+
105
+ #### `vexdo logs [task-id]`
106
+ Flags:
107
+ - `--full`: include full diffs and complete comment payloads
108
+
109
+ #### `vexdo abort`
110
+ Flags:
111
+ - `--force`: skip confirmation prompt
112
+
113
+ ## 6) Task YAML format
114
+
115
+ ```yaml
116
+ id: billing-vat-001
117
+ title: "Add VAT ID support"
118
+ depends_on: [] # optional task-level dependencies
119
+
120
+ steps:
121
+ - service: api
122
+ spec: |
123
+ Add vat_id to organization model, migration, and create/update APIs.
124
+ Ensure validation rules and API docs are updated.
125
+
126
+ - service: web
127
+ depends_on: [api] # optional per-step ordering dependency
128
+ spec: |
129
+ Add VAT ID input to organization settings screen.
130
+ Integrate with updated API and show validation errors.
131
+ ```
132
+
133
+ Field reference:
134
+ - `id` *(string, required)*: stable slug used for branches and state.
135
+ - `title` *(string, required)*: human-readable name.
136
+ - `depends_on` *(string[], optional)*: coarse dependency marker at task level.
137
+ - `steps` *(array, required)*: ordered units of work.
138
+ - `service` *(string, required)*: must match `.vexdo.yml` service name.
139
+ - `spec` *(string, required)*: concrete implementation request for Codex.
140
+ - `depends_on` *(string[], optional)*: service dependencies before this step starts.
141
+
142
+ ## 7) `.vexdo.yml` format
143
+
144
+ ```yaml
145
+ version: 1
146
+ services:
147
+ - name: api
148
+ path: ./api
149
+ - name: web
150
+ path: ./web
151
+
152
+ review:
153
+ model: claude-haiku-4-5-20251001
154
+ max_iterations: 3
155
+ auto_submit: false
156
+
157
+ codex:
158
+ model: gpt-4o
159
+ ```
160
+
161
+ Field reference:
162
+ - `version`: config schema version (currently `1`).
163
+ - `services`: list of service roots used by task steps.
164
+ - `review.model`: Claude model for reviewer + arbiter.
165
+ - `review.max_iterations`: hard cap for fix/review loop.
166
+ - `review.auto_submit`: auto-run `submit` after successful review.
167
+ - `codex.model`: model passed to Codex execution.
168
+
169
+ ## 8) Spec format guide
170
+
171
+ Good specs are:
172
+ - **specific**: list exact files, behaviors, and edge cases.
173
+ - **testable**: include acceptance checks.
174
+ - **constrained**: mention prohibited changes and compatibility limits.
175
+
176
+ Recommended template:
177
+
178
+ ```text
179
+ Goal:
180
+ Constraints:
181
+ Acceptance criteria:
182
+ - ...
183
+ - ...
184
+ Non-goals:
185
+ ```
186
+
187
+ ## 9) Troubleshooting
188
+
189
+ - **"Not inside a vexdo project"**
190
+ - Run from a directory containing `.vexdo.yml` (or use `vexdo init`).
191
+ - **Anthropic key errors**
192
+ - Ensure `ANTHROPIC_API_KEY` is exported in shell/CI.
193
+ - **Codex not found**
194
+ - Install with `npm install -g @openai/codex` and verify PATH.
195
+ - **`vexdo submit` fails with GitHub auth issues**
196
+ - Run `gh auth login` and confirm repo access.
197
+ - **Task escalated**
198
+ - Review `.vexdo/logs/*` and rerun with `vexdo fix "..."`.
199
+
200
+ ## 10) Contributing
201
+
202
+ See [CONTRIBUTING.md](./CONTRIBUTING.md).
203
+
204
+ ## 11) License
205
+
206
+ MIT.
package/bin/vexdo.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@vexdo/cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "vexdo": "./bin/vexdo.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsup",
10
+ "typecheck": "tsc --noEmit",
11
+ "lint": "eslint .",
12
+ "test": "vitest run",
13
+ "test:coverage": "vitest run"
14
+ },
15
+ "dependencies": {
16
+ "@anthropic-ai/sdk": "^0.39.0",
17
+ "@types/js-yaml": "^4.0.9",
18
+ "commander": "^12.1.0",
19
+ "js-yaml": "^4.1.1",
20
+ "ora": "^8.1.1",
21
+ "picocolors": "^1.1.1",
22
+ "yaml": "^2.6.1"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.13.10",
26
+ "@typescript-eslint/eslint-plugin": "^8.25.0",
27
+ "@typescript-eslint/parser": "^8.25.0",
28
+ "eslint": "^8.57.1",
29
+ "eslint-config-prettier": "^10.1.1",
30
+ "prettier": "^3.5.2",
31
+ "tsup": "^8.4.0",
32
+ "typescript": "^5.8.2",
33
+ "vitest": "^3.0.8"
34
+ }
35
+ }
@@ -0,0 +1,66 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import readline from 'node:readline/promises';
4
+
5
+ import type { Command } from 'commander';
6
+
7
+ import { findProjectRoot } from '../lib/config.js';
8
+ import * as logger from '../lib/logger.js';
9
+ import { clearState, loadState } from '../lib/state.js';
10
+ import { ensureTaskDirectory, moveTaskFileAtomically } from '../lib/tasks.js';
11
+
12
+ export interface AbortOptions { force?: boolean }
13
+
14
+ function fatalAndExit(message: string): never {
15
+ logger.fatal(message);
16
+ process.exit(1);
17
+ }
18
+
19
+ async function promptConfirmation(taskId: string): Promise<boolean> {
20
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
21
+ try {
22
+ const answer = await rl.question(`Abort task ${taskId}? Branches will be kept. [y/N] `);
23
+ return answer.trim().toLowerCase() === 'y';
24
+ } finally {
25
+ rl.close();
26
+ }
27
+ }
28
+
29
+ export async function runAbort(options: AbortOptions): Promise<void> {
30
+ const projectRoot = findProjectRoot();
31
+ if (!projectRoot) {
32
+ fatalAndExit('Not inside a vexdo project.');
33
+ }
34
+
35
+ const state = loadState(projectRoot);
36
+ if (!state) {
37
+ fatalAndExit('No active task.');
38
+ }
39
+
40
+ if (!options.force) {
41
+ const confirmed = await promptConfirmation(state.taskId);
42
+ if (!confirmed) {
43
+ logger.info('Abort cancelled.');
44
+ return;
45
+ }
46
+ }
47
+
48
+ const inProgressDir = path.join(projectRoot, 'tasks', 'in_progress');
49
+ if (state.taskPath.startsWith(inProgressDir) && fs.existsSync(state.taskPath)) {
50
+ const backlogDir = ensureTaskDirectory(projectRoot, 'backlog');
51
+ moveTaskFileAtomically(state.taskPath, backlogDir);
52
+ }
53
+
54
+ clearState(projectRoot);
55
+ logger.info('Task aborted. Branches preserved for manual review.');
56
+ }
57
+
58
+ export function registerAbortCommand(program: Command): void {
59
+ program
60
+ .command('abort')
61
+ .description('Abort active task')
62
+ .option('--force', 'Skip confirmation prompt')
63
+ .action(async (options: AbortOptions) => {
64
+ await runAbort(options);
65
+ });
66
+ }