@code-partner/codepipe 0.4.1-dev.128.gd6a3022 → 0.4.1-dev.129.gfffeded
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/README.md +42 -283
- package/dist/index.js +1 -1
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -1,302 +1,61 @@
|
|
|
1
1
|
# @code-partner/codepipe
|
|
2
2
|
|
|
3
|
-
**CodePipe turns
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
build and opens a PR. You control every step from a kanban dashboard (works
|
|
7
|
-
from your phone); the agent does the typing.
|
|
8
|
-
|
|
9
|
-
What you get once a project is connected:
|
|
10
|
-
|
|
11
|
-
- **A task pipeline instead of a chat.** Every task moves through explicit
|
|
12
|
-
phases — analysis → your review → implementation → build check → your
|
|
13
|
-
review → PR — visible as cards on a board. Nothing merges without you.
|
|
14
|
-
- **Approval gates.** At each gate you Approve, Reject with a comment (the
|
|
15
|
-
agent redoes the step with your instruction), or just comment.
|
|
16
|
-
- **Rework by comment.** After the PR exists, reply
|
|
17
|
-
`@codepipe rework: <what to change>` in the YouTrack task — the agent picks
|
|
18
|
-
it up and updates the PR.
|
|
19
|
-
- **Your credentials never leave your machine.** The CLI on your laptop is
|
|
20
|
-
the only thing holding YouTrack and git tokens; the orchestrating HUB has
|
|
21
|
-
no git access and no production tokens at all. Agent runners receive
|
|
22
|
-
read-only access encrypted per job and never persist it.
|
|
23
|
-
- **Works with an empty repo or an existing codebase** — you can start a
|
|
24
|
-
brand-new project or point CodePipe at years of history.
|
|
25
|
-
|
|
26
|
-
One package, one `codepipe` bin, two roles — for solo work you run both on
|
|
27
|
-
your machine with a single command (`codepipe up`):
|
|
28
|
-
|
|
29
|
-
- **CLI** — the local daemon that holds your credentials and performs every
|
|
30
|
-
write (branches, pushes, PRs, tracker updates);
|
|
31
|
-
- **worker** — the agent runner (Claude Code headless) that claims jobs and
|
|
32
|
-
does the analysis/implementation work.
|
|
33
|
-
|
|
34
|
-
---
|
|
35
|
-
|
|
36
|
-
## Before you start: the checklist
|
|
37
|
-
|
|
38
|
-
Everything you need in place before `codepipe init`:
|
|
39
|
-
|
|
40
|
-
1. **Node.js 22+** and **git** on your machine.
|
|
41
|
-
|
|
42
|
-
2. **Claude Code + a Claude subscription.** Agents run on your Claude
|
|
43
|
-
account. Install [Claude Code](https://claude.com/claude-code), make sure
|
|
44
|
-
`claude` is on `PATH`, then run `claude setup-token` and copy the token
|
|
45
|
-
(`sk-ant-…`) — the setup wizard will ask for it.
|
|
46
|
-
|
|
47
|
-
3. **A YouTrack project.** You need three things:
|
|
48
|
-
- the instance URL (e.g. `https://yourteam.youtrack.cloud`);
|
|
49
|
-
- a permanent token: YouTrack → your avatar → *Account Security* →
|
|
50
|
-
*Tokens* → *New token…*;
|
|
51
|
-
- the project key (e.g. `DEV`).
|
|
52
|
-
|
|
53
|
-
The project must have a **`Stage` custom field** (enum) — that is the
|
|
54
|
-
column set CodePipe reads and moves. The default config expects at least
|
|
55
|
-
the values `Backlog`, `Develop` and `Review`: tasks in `Develop` enter the
|
|
56
|
-
pipeline, `Backlog` shows up as the dashboard TODO section, and `Review`
|
|
57
|
-
is where a task is moved once its PR is open. (Column names are
|
|
58
|
-
configurable later in `.codepipe/project.yaml` / `regimen.yaml`.)
|
|
59
|
-
|
|
60
|
-
4. **A project repository** on **GitHub, GitLab or Bitbucket** — empty or
|
|
61
|
-
with existing code, both work — and an access token for that host:
|
|
62
|
-
- **GitHub**: a classic PAT with `repo` + `workflow`, or a fine-grained
|
|
63
|
-
PAT with *Administration RW + Contents RW + Workflows RW + Metadata R*
|
|
64
|
-
(Administration is what lets the wizard create the Context Repo for
|
|
65
|
-
you; if the repos live in an org, approve the token in the org
|
|
66
|
-
settings);
|
|
67
|
-
- **GitLab**: a PAT with `api` (or `read_api` + `write_repository`);
|
|
68
|
-
- **Bitbucket**: your Atlassian account email + a scoped API token
|
|
69
|
-
(id.atlassian.com → *Security* → *API tokens* → *Create API token with
|
|
70
|
-
scopes*, app *Bitbucket*): `read:account`, `read:repository` +
|
|
71
|
-
`write:repository`, `write:pullrequest`, plus `admin:repository` so the
|
|
72
|
-
wizard can create the Context Repo.
|
|
73
|
-
|
|
74
|
-
5. **A CodePipe account** — nothing to prepare: it is created automatically
|
|
75
|
-
during setup from your email. During the beta, new accounts wait for
|
|
76
|
-
operator approval (you get an email when it happens).
|
|
3
|
+
**CodePipe turns tasks into reviewed pull requests.** An AI agent (Claude
|
|
4
|
+
Code) analyzes a task, waits for your approval, implements it and opens a
|
|
5
|
+
PR — every step under your control, from your phone or your terminal.
|
|
77
6
|
|
|
78
7
|
## Install
|
|
79
8
|
|
|
80
9
|
```bash
|
|
81
10
|
npm i -g @code-partner/codepipe
|
|
11
|
+
codepipe init # setup wizard; on finish it starts `codepipe up` itself
|
|
82
12
|
```
|
|
83
13
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
## Step 1 — register the project: `codepipe init`
|
|
88
|
-
|
|
89
|
-
Two ways to start, depending on where your code is:
|
|
90
|
-
|
|
91
|
-
```bash
|
|
92
|
-
# A) fresh workspace — repos will be cloned for you
|
|
93
|
-
mkdir ~/Projects/acme && cd ~/Projects/acme
|
|
94
|
-
codepipe init
|
|
95
|
-
|
|
96
|
-
# B) you already have the code checked out — run it inside the repo
|
|
97
|
-
cd ~/Projects/acme-api
|
|
98
|
-
codepipe init
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
In case B the wizard detects `origin`, prefills the repo questions and
|
|
102
|
-
registers the checkout in place — no duplicate clone; `.codepipe/` is created
|
|
103
|
-
inside the repo and offered for its `.gitignore`.
|
|
104
|
-
|
|
105
|
-
The wizard asks, in order (Ctrl+C is safe at any point — a re-run resumes
|
|
106
|
-
from where you stopped):
|
|
107
|
-
|
|
108
|
-
1. **Where should this project run** — pick `local` for solo work (`farm` is
|
|
109
|
-
for hosting the daemon on a separate always-on machine).
|
|
110
|
-
2. **HUB URL** — accept the default unless you self-host.
|
|
111
|
-
3. **Your account email** — a browser tab opens to confirm this CLI with one
|
|
112
|
-
click; no account yet means one is created and confirmed by email (plus
|
|
113
|
-
operator approval during the beta — the wizard waits, and it is safe to
|
|
114
|
-
Ctrl+C and re-run later).
|
|
115
|
-
4. **Project id and name** — a short slug (e.g. `acme`) and a display name.
|
|
116
|
-
5. **Automation level** — `assisted`: every phase starts only when you press
|
|
117
|
-
Run on the dashboard (recommended to learn the ropes); `full`: tasks run
|
|
118
|
-
end-to-end to a PR, pausing only at gates you keep. You can change it
|
|
119
|
-
later.
|
|
120
|
-
6. **YouTrack URL, token and project key** — checked live before moving on.
|
|
121
|
-
7. **Allow CodePipe to write to YouTrack?** — yes: it moves tasks between
|
|
122
|
-
columns and comments the PR link; no: YouTrack stays strictly read-only
|
|
123
|
-
(you move cards yourself).
|
|
124
|
-
8. **Project repositories** — paste a clone URL per repo; the host token is
|
|
125
|
-
asked once per host and verified live. Connect one repo or several.
|
|
126
|
-
9. **Context Repo** — the git repo where CodePipe keeps its prompts,
|
|
127
|
-
knowledge base and task artifacts (analysis, patches). Accept the default
|
|
128
|
-
name and the wizard **creates it next to your first project repo**; or
|
|
129
|
-
attach to an existing one.
|
|
130
|
-
10. **Your YouTrack login** — only tasks assigned to you enter the pipeline
|
|
131
|
-
(lift the filter later with `require_assignee: false` in
|
|
132
|
-
`.codepipe/project.yaml`).
|
|
133
|
-
11. **Claude OAuth token** — paste the `claude setup-token` output. Skippable,
|
|
134
|
-
but no agent job runs until you set it (`codepipe login` does it later).
|
|
135
|
-
|
|
136
|
-
Then init does the actual setup — this is the "first run" work:
|
|
137
|
-
|
|
138
|
-
- **creates (or attaches to) the Context Repo** on your git host and
|
|
139
|
-
scaffolds it;
|
|
140
|
-
- **registers the project on the HUB** under your account (re-running init
|
|
141
|
-
on an existing project is safe — it reuses it);
|
|
142
|
-
- **clones the Context Repo** into `.codepipe/` and writes your local config
|
|
143
|
-
next to it: `project.yaml` (repos, board columns, sync rules) and
|
|
144
|
-
`regimen.yaml` (automation, gates, build/CI loops) — plain YAML you can
|
|
145
|
-
edit any time;
|
|
146
|
-
- **clones the project repos** as siblings of `.codepipe/` (skipped for an
|
|
147
|
-
in-place init — your checkout is the workspace);
|
|
148
|
-
- **saves secrets** to `~/.codepipe/secrets/<project>.json` (mode 0600 —
|
|
149
|
-
tokens never land in a repo);
|
|
150
|
-
- **offers to index the codebase** — a one-time agent job that writes
|
|
151
|
-
`knowledge/INDEX.md` into the Context Repo, a map of your code that makes
|
|
152
|
-
every later analysis faster and better grounded. Say yes; it runs in the
|
|
153
|
-
background once the daemon and worker are up.
|
|
154
|
-
|
|
155
|
-
## Step 2 — start it: `codepipe up`
|
|
156
|
-
|
|
157
|
-
```bash
|
|
158
|
-
cd ~/Projects/acme # the folder you ran init in
|
|
159
|
-
codepipe up
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
One command, both roles: the CLI daemon connects to the HUB, and an embedded
|
|
163
|
-
agent worker registers itself automatically on the first run (no dashboard
|
|
164
|
-
visit, no manual tokens) and starts claiming jobs. Leave it running while you
|
|
165
|
-
work; Ctrl+C stops both.
|
|
14
|
+
`init` runs `codepipe up` for you at the end — no need to run `up` separately
|
|
15
|
+
after setup.
|
|
166
16
|
|
|
167
|
-
|
|
168
|
-
with the daemon embedded — see the command list below.
|
|
17
|
+
## Requirements
|
|
169
18
|
|
|
170
|
-
|
|
19
|
+
- **Node.js 22+** and **git**.
|
|
20
|
+
- **Claude Code + an active Claude subscription.** Agents run on your Claude
|
|
21
|
+
account: install [Claude Code](https://claude.com/claude-code), make sure
|
|
22
|
+
`claude` is on `PATH`, then run `claude setup-token` — the wizard asks for
|
|
23
|
+
the token.
|
|
24
|
+
- **A git host token** (GitHub / GitLab / Bitbucket) — for branches, pull
|
|
25
|
+
requests and the Context Repo. The wizard tells you which scopes it needs
|
|
26
|
+
and verifies the token live.
|
|
27
|
+
- *Optional:* **podman** — only for the BUILD phase (an automatic
|
|
28
|
+
lint/build/test run before the PR). The solo workflow works without it.
|
|
29
|
+
- **Task tracker — built-in.** CodePipe ships with its own task tracker, so
|
|
30
|
+
there is nothing to set up; connecting YouTrack is optional.
|
|
171
31
|
|
|
172
|
-
|
|
173
|
-
project card is there, with the setup/index progress on it.
|
|
174
|
-
2. In YouTrack, take a task, **assign it to yourself** and move it to
|
|
175
|
-
**`Develop`** (or create it in `Backlog` and press *Develop* on the
|
|
176
|
-
dashboard card).
|
|
177
|
-
3. Watch it on the board: the agent analyzes the task and produces
|
|
178
|
-
`SOLUTION.md` (+ questions, if any). In `assisted` mode press **Run** to
|
|
179
|
-
start each phase.
|
|
180
|
-
4. **Review gate:** read the proposed solution, then Approve — or Reject
|
|
181
|
-
with a comment and get a revised one.
|
|
182
|
-
5. The agent implements, the build check runs lint/build/tests, and after
|
|
183
|
-
your second approval the CLI pushes the branch and **opens the PR**
|
|
184
|
-
(task moves to `Review` with a PR-link comment, if writes are on).
|
|
185
|
-
6. Want changes after reviewing the PR? Comment in the YouTrack task:
|
|
32
|
+
## Commands
|
|
186
33
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
34
|
+
| Command | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `codepipe init` | set up a project (wizard) and start it |
|
|
37
|
+
| `codepipe up` | run the local daemon + agent worker (solo mode) |
|
|
38
|
+
| `codepipe` | open an interactive REPL with the daemon embedded |
|
|
39
|
+
| `codepipe sync` | clone/fetch the project repos |
|
|
40
|
+
| `codepipe login` | set or replace your Claude token |
|
|
41
|
+
| `codepipe clone <id>` | rebuild the workspace on another machine |
|
|
190
42
|
|
|
191
|
-
|
|
43
|
+
Advanced and fleet commands (separate daemon/worker, multi-project farms) are
|
|
44
|
+
covered in the docs.
|
|
192
45
|
|
|
193
|
-
##
|
|
46
|
+
## What happens next
|
|
194
47
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
codepipe sync clone/fetch the project repos per project.yaml
|
|
200
|
-
codepipe login set/replace the Claude token (--shared for machine-wide)
|
|
201
|
-
codepipe clone <id> reconstruct the workspace on another machine
|
|
202
|
-
codepipe project move … repoint an initialised project at another HUB
|
|
203
|
-
codepipe worker … run the agent worker separately (see below)
|
|
204
|
-
```
|
|
48
|
+
Create a task (or pick an existing one) and watch it on the board: the agent
|
|
49
|
+
studies your codebase and proposes a solution as `SOLUTION.md`. Nothing moves
|
|
50
|
+
past that point without you — **you Approve**, or Reject with a comment and
|
|
51
|
+
get a revised proposal.
|
|
205
52
|
|
|
206
|
-
|
|
53
|
+
After your approval the agent implements the change, runs its checks and
|
|
54
|
+
opens a **pull request** for you to review. Rework is a comment away: reply
|
|
55
|
+
on the task and the agent updates the PR. Details of the `init` wizard,
|
|
56
|
+
automation levels and everything beyond the solo setup live in the docs.
|
|
207
57
|
|
|
208
|
-
|
|
209
|
-
/status connection, project, Context Repo branch, siblings
|
|
210
|
-
/tasks [--phase P] list tasks
|
|
211
|
-
/task <key> task detail
|
|
212
|
-
/sync clone/fetch project repos per project.yaml
|
|
213
|
-
/apply <key> apply a task patch on a new branch + commit (local mode)
|
|
214
|
-
/unstash <key> apply a task patch uncommitted on the base commit
|
|
215
|
-
/applied <key> --pr U mark applied (repo=URL form for multi-repo tasks)
|
|
216
|
-
/abort <key> --reason roll the local apply back, send the task to the agent
|
|
217
|
-
/secret set|list|unset manage stored tokens
|
|
218
|
-
/quit stop the daemon and exit
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
`/apply` and `/unstash` support the **local apply mode** (`apply.mode: local`
|
|
222
|
-
in `project.yaml`, or per-task with `@codepipe apply: local`): CodePipe never
|
|
223
|
-
pushes for you — you review the staged change locally, commit, push and open
|
|
224
|
-
the PR yourself, then `/applied <key> --pr <url>`.
|
|
225
|
-
|
|
226
|
-
## Running the worker elsewhere (optional)
|
|
227
|
-
|
|
228
|
-
Solo mode embeds the worker, but you can also run it on a separate machine —
|
|
229
|
-
a desktop with spare cores, a home server, a cloud box:
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
codepipe worker # interactive wizard on an unregistered machine
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
Log in by email, pick "all my projects" or a subset — runners are bound to
|
|
236
|
-
your account and only serve your projects. Headless servers register
|
|
237
|
-
non-interactively:
|
|
238
|
-
|
|
239
|
-
```bash
|
|
240
|
-
codepipe worker register \
|
|
241
|
-
--hub https://hub.example.com \ # env CODEPIPE_HUB_URL
|
|
242
|
-
--token <registration token> \ # env CODEPIPE_WORKER_REG_TOKEN
|
|
243
|
-
--llm claude-headless # env CODEPIPE_WORKER_LLM
|
|
244
|
-
# --name / CODEPIPE_WORKER_NAME, --projects / CODEPIPE_WORKER_PROJECTS
|
|
245
|
-
# prints the key fingerprint — confirm it on the CLI (pin-on-register)
|
|
246
|
-
|
|
247
|
-
codepipe worker # connect to HUB and run the claim-loop (= worker start)
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
The worker config lives at `~/.codepipe/worker/config.json`
|
|
251
|
-
(`--profile <id>` → `~/.codepipe/worker/profiles/<id>/config.json`;
|
|
252
|
-
`CODEPIPE_SANDBOX_CONFIG` overrides). For the agent backend, Claude Code
|
|
253
|
-
(`claude`) must be on `PATH`. On a fresh Linux server use the installer
|
|
254
|
-
served by your HUB instead
|
|
255
|
-
(`curl -fsSL https://<your-hub>/install-sandbox.sh | sudo bash -s -- …`) —
|
|
256
|
-
it also sets up Node 22, the agent toolchain, rootless Podman, an isolated
|
|
257
|
-
`devpipe` user and systemd units.
|
|
258
|
-
|
|
259
|
-
## Where things live
|
|
260
|
-
|
|
261
|
-
- `.codepipe/` in the workspace — the cloned Context Repo, your local
|
|
262
|
-
`project.yaml` / `regimen.yaml`, plus runtime state (gitignored).
|
|
263
|
-
- Project repos — siblings of `.codepipe/` (or the checkout itself for an
|
|
264
|
-
in-place init).
|
|
265
|
-
- `~/.codepipe/secrets/<project>.json` — tokens, mode 0600, never in a repo.
|
|
266
|
-
- `~/.codepipe/worker/config.json` — worker registration (HUB URL, runner
|
|
267
|
-
id, keypair), mode 0600.
|
|
268
|
-
|
|
269
|
-
## Continuing on another machine
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
mkdir ~/Projects/acme && cd ~/Projects/acme
|
|
273
|
-
codepipe clone acme # re-clone the workspace; re-enter your tokens
|
|
274
|
-
```
|
|
275
|
-
|
|
276
|
-
## Moving a project to another HUB
|
|
277
|
-
|
|
278
|
-
When a project graduates from a shared HUB to an enterprise one (or you want to
|
|
279
|
-
try it on a dev stand), `project move` re-registers it on the target HUB without
|
|
280
|
-
the full `init` wizard. Your Context Repo, project config and stored
|
|
281
|
-
credentials are reused as-is; only a fresh account sign-in on the target HUB is
|
|
282
|
-
needed.
|
|
283
|
-
|
|
284
|
-
```bash
|
|
285
|
-
codepipe project move --from-profile acme --hub https://hub.enterprise.example
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
That repoints the `acme` profile in place. To keep the old binding and add a
|
|
289
|
-
second one — e.g. a dev stand alongside prod — write a separate profile:
|
|
290
|
-
|
|
291
|
-
```bash
|
|
292
|
-
codepipe project move --from-profile acme --to-profile acme-dev \
|
|
293
|
-
--hub https://hub.dev.example --project acme-dev
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
The move is non-destructive: the project's registration on the old HUB stays.
|
|
297
|
-
Task history is not copied — the project starts clean on the target HUB — and
|
|
298
|
-
the source HUB must be reachable so the current definition can be read.
|
|
299
|
-
`--account <email>` targets a specific account on the new HUB (defaults to the
|
|
300
|
-
source account); `--yes` skips the confirmation.
|
|
58
|
+
---
|
|
301
59
|
|
|
302
|
-
|
|
60
|
+
Full docs, architecture and the fleet/multi-project setup →
|
|
61
|
+
<https://codepipe.dev>
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var bf=Object.defineProperty;var R=(t,e)=>()=>(t&&(e=t(t=0)),e);var $f=(t,e)=>{for(var r in e)bf(t,r,{get:e[r],enumerable:!0})};var xe,Ge,Ko=R(()=>{"use strict";xe="0.4.1-dev.
|
|
2
|
+
var bf=Object.defineProperty;var R=(t,e)=>()=>(t&&(e=t(t=0)),e);var $f=(t,e)=>{for(var r in e)bf(t,r,{get:e[r],enumerable:!0})};var xe,Ge,Ko=R(()=>{"use strict";xe="0.4.1-dev.129.gfffeded",Ge="https://hub.dev.codepipe.dev"});function Tn(t){return typeof t!="number"||!Number.isFinite(t)?!1:t>=vf&&t<=Fe}var Fe,vf,Es=R(()=>{"use strict";Ko();Fe=4,vf=4});var js=R(()=>{"use strict";Ko()});function In(t=3){return`# CodePipe Context Repo format version.
|
|
3
3
|
# Bumped by the scaffold / the format-migration PR \u2014 do not edit by hand.
|
|
4
4
|
template_version: ${t}
|
|
5
5
|
`}function _s(t){if(!t)return 0;let e=/^\s*template_version:\s*(\d+)\s*$/m.exec(t),r=e?parseInt(e[1]??"",10):NaN;return Number.isFinite(r)&&r>=0?r:0}function Ns(t,e=As){let r=[];for(let n of e)if(!(n.version<=t))for(let o of n.addedPaths)r.includes(o)||r.push(o);return r}function Os(t,e=As){return e.filter(r=>r.version>t)}var He,Ur,As,Ds=R(()=>{"use strict";He=".codepipe-context.yaml",Ur=".devpipe-context.yaml",As=[{version:1,summary:"ADR section: adr/ scaffold (README, template, derived INDEX) + the format-version stamp itself",addedPaths:["adr/README.md","adr/0000-template.md","adr/INDEX.md",He]},{version:2,summary:"Rename the format-version stamp .devpipe-context.yaml \u2192 .codepipe-context.yaml (drop the devpipe-brand leftover)",addedPaths:[He]},{version:3,summary:"Custom project actions (the RUN tab): actions/ scaffold with the format README",addedPaths:["actions/README.md"]}]});var Us=R(()=>{"use strict"});var xf,zb,Ls=R(()=>{"use strict";xf=[{id:"intake",label:"Intake",phases:["INTAKE"]},{id:"analysis",label:"Analysis",phases:["ANALYZING","ESTIMATING"]},{id:"ready",label:"Ready",phases:["READY"]},{id:"implementation",label:"Implementation",phases:["IMPLEMENTING","BUILD"]},{id:"delivery",label:"Delivery",phases:["READY_TO_APPLY","APPLYING"]}],zb=xf.flatMap(t=>t.phases)});function Et(){return{inputTokens:0,outputTokens:0,cacheReadInputTokens:0,cacheCreationInputTokens:0,totalCostUsd:0,model:"",numTurns:0,durationMs:0}}var Bs,Wo=R(()=>{"use strict";Bs=["estimate","analyze","implement","resolve-conflict","rework","draft","build","code-review","index","backfill","analysis","chat"]});var Ke,Cn,Sf,Yo,Ms,qo=R(()=>{"use strict";Ke="opus",Cn="sonnet",Sf="haiku",Yo=[Ke,Cn,Sf],Ms={analyze:Ke,implement:Ke,"resolve-conflict":Ke,rework:Ke,estimate:Cn,build:Cn,"code-review":Ke,draft:Cn,index:Ke,backfill:Ke,analysis:Ke,chat:Ke}});function pt(t){return Rf.has(t)?!0:Tf.some(e=>t.startsWith(e))}function Pn(t,e){if(!t||typeof t!="object"||Array.isArray(t))throw new Error(`${e} must be a mapping`);return t}function jt(t,e){if(t!=null){if(typeof t!="string")throw new Error(`${e} must be a string`);return t}}function Vo(t,e){if(t!=null){if(typeof t!="number"||!Number.isFinite(t))throw new Error(`${e} must be a number`);return t}}function Ws(t,e){let r=Pn(e,"action definition"),n=t.replace(/\.(yml|yaml)$/i,""),o=jt(r.id,"id")??n;if(!Gs.test(o))throw new Error(`id must match ${Gs} (got "${o}")`);if(o!==n)throw new Error(`id "${o}" must match the file name ("${n}")`);let i=jt(r.title,"title")?.trim()||o,s;if(r.env!==void 0&&r.env!==null){let d=Pn(r.env,"env");s={};for(let[u,m]of Object.entries(d)){if(!Se.test(u))throw new Error(`env name "${u}" must match ${Se}`);if(typeof m!="string"&&typeof m!="number"&&typeof m!="boolean")throw new Error(`env.${u} must be a scalar`);s[u]=String(m)}}let a,c=r.params;if(c!=null){if(!Array.isArray(c))throw new Error("params must be a list");if(c.length>Fs)throw new Error(`too many params (max ${Fs})`);let d=new Set;a=c.map((u,m)=>If(u,m,d))}let l=r.steps;if(!Array.isArray(l)||l.length===0)throw new Error("steps must be a non-empty list");if(l.length>Hs)throw new Error(`too many steps (max ${Hs})`);let p=l.map((d,u)=>{let m=Pn(d,`steps[${u}]`),f=jt(m.run,`steps[${u}].run`);if(!f||f.trim().length===0)throw new Error(`steps[${u}].run is required`);let y=jt(m.name,`steps[${u}].name`),w=Vo(m.timeout_sec,`steps[${u}].timeout_sec`);if(w!==void 0&&(!Number.isInteger(w)||w<=0))throw new Error(`steps[${u}].timeout_sec must be a positive integer`);return{run:f,...y?{name:y}:{},...w!==void 0?{timeout_sec:w}:{}}});return{id:o,title:i,...s?{env:s}:{},...a?{params:a}:{},steps:p}}function If(t,e,r){let n=Pn(t,`params[${e}]`),o=jt(n.name,`params[${e}].name`);if(!o||!Se.test(o))throw new Error(`params[${e}].name must match ${Se}`);if(r.has(o))throw new Error(`duplicate param "${o}"`);r.add(o);let i=n.type;if(i!=="string"&&i!=="enum"&&i!=="number"&&i!=="boolean")throw new Error(`params[${e}].type must be string | enum | number | boolean`);let s;if(i==="enum"){let m=n.enum;if(!Array.isArray(m)||m.length===0||!m.every(f=>typeof f=="string"))throw new Error(`params[${e}].enum must be a non-empty string list`);s=m}let a=jt(n.label,`params[${e}].label`),c=jt(n.help,`params[${e}].help`),l=n.required===!0,p=Vo(n.min,`params[${e}].min`),d=Vo(n.max,`params[${e}].max`),u=n.default;if(u!=null&&typeof u!="string"&&typeof u!="number"&&typeof u!="boolean")throw new Error(`params[${e}].default must be a scalar`);return{name:o,type:i,...a?{label:a}:{},...c?{help:c}:{},...l?{required:!0}:{},...s?{enum:s}:{},...p!==void 0?{min:p}:{},...d!==void 0?{max:d}:{},...u!=null?{default:u}:{}}}function Ys(t,e){let r=t.params??[],n=new Map(r.map(i=>[i.name,i]));for(let i of Object.keys(e))if(!n.has(i))return{ok:!1,error:`unknown param: ${i}`};let o={};for(let i of r){let s=e[i.name];if(s==null||typeof s=="string"&&s.trim()==="")if(i.default!==void 0)s=i.default;else{if(i.required)return{ok:!1,error:`${i.name} is required`};continue}switch(i.type){case"string":{if(typeof s!="string")return{ok:!1,error:`${i.name} must be a string`};o[i.name]=s;break}case"enum":{if(typeof s!="string"||!(i.enum??[]).includes(s))return{ok:!1,error:`${i.name} must be one of: ${(i.enum??[]).join(", ")}`};o[i.name]=s;break}case"number":{let a=typeof s=="number"?s:typeof s=="string"?Number(s):NaN;if(!Number.isFinite(a))return{ok:!1,error:`${i.name} must be a number`};if(i.min!==void 0&&a<i.min)return{ok:!1,error:`${i.name} must be \u2265 ${i.min}`};if(i.max!==void 0&&a>i.max)return{ok:!1,error:`${i.name} must be \u2264 ${i.max}`};o[i.name]=a;break}case"boolean":{let a=typeof s=="boolean"?s:s==="true"?!0:s==="false"?!1:null;if(a===null)return{ok:!1,error:`${i.name} must be a boolean`};o[i.name]=a;break}}}return{ok:!0,values:o}}var Se,Rf,Tf,Ks,Gs,Fs,Hs,Jo=R(()=>{"use strict";Se=/^[A-Z][A-Z0-9_]*$/,Rf=new Set(["GH_TOKEN","GITHUB_TOKEN","GITLAB_TOKEN"]),Tf=["BITBUCKET_","YOUTRACK_","CLAUDE_","INPUT_"];Ks=600,Gs=/^[a-z0-9-]+$/,Fs=32,Hs=64});function Js(t=Cf){let e=[];for(let r of t)e.push(...r.fields),r.groups&&e.push(...Js(r.groups));return e}function Pf(t,e){let r=JSON.stringify(e);return Js().find(n=>n.file===t&&JSON.stringify(n.path)===r)}function zs(t){let e=Pf(t.file,t.path);if(!e)return{ok:!1,error:`unknown setting path: ${t.file}:${t.path.join(".")}`};let r=t.value,n=e.type==="model"||e.type==="enum"||e.type==="string";if(r===null||n&&typeof r=="string"&&r.trim().length===0)return e.nullable?{ok:!0,field:e,value:null}:{ok:!1,error:`${e.id} cannot be cleared`};switch(e.type){case"model":case"string":{if(typeof r!="string")return{ok:!1,error:`${e.id} must be a string`};let i=r.trim();return e.type==="string"&&e.mustContain&&!i.includes(e.mustContain)?{ok:!1,error:`${e.id} must contain "${e.mustContain}"`}:{ok:!0,field:e,value:i}}case"enum":{if(typeof r!="string")return{ok:!1,error:`${e.id} must be a string`};let i=r.trim();return e.suggestions?.includes(i)?{ok:!0,field:e,value:i}:{ok:!1,error:`${e.id} must be one of: ${(e.suggestions??[]).join(", ")}`}}case"number":return typeof r!="number"||!Number.isFinite(r)||!Number.isInteger(r)?{ok:!1,error:`${e.id} must be an integer`}:e.min!==void 0&&r<e.min?{ok:!1,error:`${e.id} must be \u2265 ${e.min}`}:e.max!==void 0&&r>e.max?{ok:!1,error:`${e.id} must be \u2264 ${e.max}`}:{ok:!0,field:e,value:r};case"boolean":return typeof r!="boolean"?{ok:!1,error:`${e.id} must be a boolean`}:{ok:!0,field:e,value:r};case"string_list":{if(!Array.isArray(r))return{ok:!1,error:`${e.id} must be a list`};let i=r.map(s=>typeof s=="string"?s.trim():"").filter(s=>s.length>0);return i.length===0?e.nullable?{ok:!0,field:e,value:null}:{ok:!1,error:`${e.id} must be a non-empty list`}:{ok:!0,field:e,value:i}}case"string_map":{if(typeof r!="object"||r===null||Array.isArray(r))return{ok:!1,error:`${e.id} must be a name\u2192value map`};let i=Object.entries(r);if(i.length===0)return e.nullable?{ok:!0,field:e,value:null}:{ok:!1,error:`${e.id} must be a non-empty map`};if(i.length>qs)return{ok:!1,error:`${e.id} must have \u2264 ${qs} entries`};let s={};for(let[a,c]of i){if(!Se.test(a))return{ok:!1,error:`${e.id}: name "${a}" must match ${Se}`};if(pt(a))return{ok:!1,error:`${e.id}: name "${a}" is reserved`};if(typeof c!="string")return{ok:!1,error:`${e.id}: value of "${a}" must be a string`};if(c.length>Vs)return{ok:!1,error:`${e.id}: value of "${a}" must be \u2264 ${Vs} chars`};s[a]=c}return{ok:!0,field:e,value:s}}}}var qs,Vs,Cf,Xs=R(()=>{"use strict";Jo();Wo();qo();qs=64,Vs=2e3,Cf=[{id:"branch",label:"Branch",help:"Task-branch naming.",fields:[{id:"branch.pattern",file:"project.yaml",path:["branch","pattern"],type:"string",label:"Task-branch pattern",nullable:!0,mustContain:"<TASK_ID>",help:"Template for the per-task branch name. Must contain the <TASK_ID> placeholder. Empty \u21D2 default codepipe/<TASK_ID>."}]},{id:"llm",label:"LLM",fields:[],groups:[{id:"llm.models",label:"Models by role",help:"Model per pipeline stage. Empty \u21D2 the role's recommended default applies.",fields:[{id:"llm.models.default",file:"regimen.yaml",path:["llm","models","default"],type:"model",label:"default (catch-all)",nullable:!0,suggestions:[...Yo],help:"Overrides the recommended defaults for every role not listed below."},...Bs.map(t=>({id:`llm.models.${t}`,file:"regimen.yaml",path:["llm","models",t],type:"model",label:t,nullable:!0,suggestions:[...Yo],help:`recommended: ${Ms[t]}`}))]}]},{id:"variables",label:"Variables",help:"Non-secret env vars for custom actions (RUN tab). Secrets go via the sealed Secrets form on the Tokens section instead.",fields:[{id:"variables",file:"project.yaml",path:["variables"],type:"string_map",label:"Variables",nullable:!0,help:"NAME=value pairs, one per line. Names are env-style (A-Z, 0-9, _); reserved credential names are refused."}]},{id:"gates",label:"Gates",help:"Approval gate before the pipeline starts (orthogonal to automation).",fields:[{id:"gates.require_approval",file:"regimen.yaml",path:["gates","require_approval"],type:"boolean",label:"Require approval before start",nullable:!1,help:"When on, a task taken from Develop waits in AWAITING_APPROVAL for a manual Approve before analysis starts. Off \u21D2 intake auto-starts the pipeline."}]},{id:"automation",label:"Automation",help:"How far the pipeline advances the G1\u2013G4 gates without a manual Run/Approve. Independent of the approval gate above.",fields:[{id:"automation.level",file:"regimen.yaml",path:["automation","level"],type:"enum",label:"Automation level",nullable:!1,suggestions:["assisted","full"],help:"assisted \u21D2 manual Run/Approve at each of G1\u2013G4; full \u21D2 gates auto-pass."}]}]});var Qs=R(()=>{"use strict"});var Zs=R(()=>{"use strict"});var ea=R(()=>{"use strict"});var ta=R(()=>{"use strict"});function ra(t){return!(!t||t.length>250||!/^[A-Za-z0-9._/-]+$/.test(t)||/^[-/.]/.test(t)||/[/.]$/.test(t)||t.endsWith(".lock")||t.includes("..")||t.includes("//")||t.includes("/."))}function et(t){return`${t}${Ef}`}var At,Ef,na=R(()=>{"use strict";At=/^[a-z][a-z0-9-]{1,30}$/,Ef="-codepipe-ctx"});var oa=R(()=>{"use strict"});var ia=R(()=>{"use strict"});function qt(t){for(let e of jf){let r=t.match(e.pattern);if(r)return{name:e.name,args:(r[1]??"").trim()}}return null}var jf,sa=R(()=>{"use strict";jf=[{name:"rework",pattern:/^@(?:codepipe|devpipe)\s+rework:\s*(.+)$/im,description:"Send the task back to ANALYZING with a new instruction.",hasArg:!0},{name:"approve",pattern:/^@(?:codepipe|devpipe)\s+approve\s*$/im,description:"Merge every PR for this task (TRACKING only)."},{name:"reject",pattern:/^@(?:codepipe|devpipe)\s+reject(?::\s*(.+))?\s*$/im,description:"Abandon this task: close any open PR(s) and move it to DONE (reason rejected_and_closed). Valid from any non-terminal phase; from early phases it just skips the task.",hasArg:!0},{name:"merge",pattern:/^@(?:codepipe|devpipe)\s+merge\s*$/im,description:"Merge the task branch into the default branch without a PR (direct apply path, apply.pull_request=false; TRACKING only)."},{name:"rebase",pattern:/^@(?:codepipe|devpipe)\s+rebase\s*$/im,description:"Re-apply the patch on top of the latest base branch and force-push. Use after a PR merge conflict or APPLYING failure."},{name:"update",pattern:/^@(?:codepipe|devpipe)\s+update(?::\s*(.+))?\s*$/im,description:"Update a conflicting PR: resolve conflicts against the current base branch (TRACKING only). Optional `: <instruction>` is forwarded to the agent.",hasArg:!0},{name:"apply",pattern:/^@(?:codepipe|devpipe)\s+apply:\s*(local|auto)\s*$/im,description:"Override apply mode for this task: local (you apply by hand) or auto.",hasArg:!0}]});var aa=R(()=>{"use strict"});var ca=R(()=>{"use strict"});var la=R(()=>{"use strict"});function ua(t){return t.toUpperCase().replace(/[^A-Z0-9]/g,"").replace(/^[^A-Z]+/,"").slice(0,10)}function da(t){let e=t.split(/[^A-Za-z0-9]+/).filter(Boolean),r=ua(e.length>=2?e.map(o=>o[0]).join(""):(e[0]??"").slice(0,4));if(Vt.test(r))return r;let n=ua(t);return Vt.test(n)?n:zo}function fa(t,e){let r=new Set([...e].map(n=>n.toUpperCase()));if(!r.has(t.toUpperCase()))return t;for(let n=1;n<1e4;n++){let o=String(n),i=t.slice(0,10-o.length)+o;if(!r.has(i.toUpperCase()))return i}return t}function Br(t){return`tasks/${t}/issue.yaml`}var pa,En,zo,Vt,Xo,u$,jn,Lr,ma=R(()=>{"use strict";pa=["bug","feature","epic"],En="feature",zo="TASK",Vt=/^[A-Z][A-Z0-9]{1,9}$/;Xo={backlog:"Backlog",source:["To Do"],inProgress:"In Progress",review:"Review",done:["Done"],rejected:["Rejected"],statusColumns:["Backlog","To Do","In Progress","Review","Done","Rejected"]},u$=Xo.backlog,jn="tracker/config.yaml",Lr="tracker/counter"});var ga=R(()=>{"use strict"});function Jt(t){let e=t.match(/^git@([^:/]+):/);if(e)return e[1]??null;let r=t.match(/^ssh:\/\/(?:[^@/]+@)?([^/:]+)/);if(r)return r[1]??null;let n=t.match(/^https?:\/\/(?:[^@/]+@)?([^/:]+)/);return n?n[1]??null:null}function An(t){switch(t){case Mr.gitlab:return"oauth2";case Mr.bitbucket:return"x-token-auth";default:return"x-access-token"}}var Mr,_t,ha=R(()=>{"use strict";Mr={github:"github.com",gitlab:"gitlab.com",bitbucket:"bitbucket.org"};_t="x-bitbucket-api-token-auth"});var ya=R(()=>{"use strict"});var wa=R(()=>{"use strict";ga();ha();ya()});var ka,q=R(()=>{"use strict";Es();js();Ds();Us();Ls();Wo();qo();Xs();Jo();Qs();Zs();ea();ta();na();oa();ia();sa();aa();ca();la();ma();wa();ka=20});function K(t){return process.env[`CODEPIPE_${t}`]??process.env[`DEVPIPE_${t}`]}var Ne=R(()=>{"use strict"});import{existsSync as ba,renameSync as Af}from"node:fs";function Nt(t,e,r=n=>console.error(n)){if(ba(e)||!ba(t))return!1;try{return Af(t,e),r(`migrated ${t} \u2192 ${e} (DevPipe \u2192 CodePipe)`),!0}catch(n){return r(`failed to migrate ${t} \u2192 ${e}: ${n instanceof Error?n.message:String(n)} \u2014 continuing with the legacy path untouched`),!1}}var _n=R(()=>{"use strict"});import{existsSync as Nn,readFileSync as $a,readdirSync as _f,writeFileSync as Nf}from"node:fs";import{dirname as Of,join as dt}from"node:path";import{parse as Df}from"yaml";function Uf(t=process.cwd()){let e=t;for(;;){if(Nn(dt(e,We,"project.yaml")))return e;if(Nn(dt(e,Qo,"project.yaml")))return Nt(dt(e,Qo),dt(e,We))&&Bf(e),e;let r=Of(e);if(r===e)return null;e=r}}function Lf(t){let e=dt(t,We);return Nn(e)?e:dt(t,Qo)}function Bf(t){let e=dt(t,".gitignore");if(!Nn(e))return;let r=$a(e,"utf-8"),n=r.split(/\r?\n/).map(o=>{let i=o.trim();return i===".devpipe"||i===".devpipe/"||i==="/.devpipe"||i==="/.devpipe/"?o.replace(".devpipe",".codepipe"):o}).join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@code-partner/codepipe",
|
|
3
|
-
"version": "0.4.1-dev.
|
|
3
|
+
"version": "0.4.1-dev.129.gfffeded",
|
|
4
|
+
"description": "CodePipe turns tasks into reviewed pull requests: an AI agent analyzes, implements, and opens a PR under your control.",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"main": "./dist/index.js",
|
|
6
7
|
"bin": {
|
|
@@ -28,8 +29,8 @@
|
|
|
28
29
|
"@types/ws": "^8.5.13",
|
|
29
30
|
"esbuild": "^0.21.5",
|
|
30
31
|
"vitest": "2.1.9",
|
|
31
|
-
"@codepipe/
|
|
32
|
-
"@codepipe/
|
|
32
|
+
"@codepipe/shared": "0.0.0",
|
|
33
|
+
"@codepipe/sandbox": "0.1.0"
|
|
33
34
|
},
|
|
34
35
|
"scripts": {
|
|
35
36
|
"build": "tsc -p tsconfig.json && chmod +x dist/index.js",
|