@cxi-lmai/ci-agent-platform 3.0.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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +219 -0
  3. package/bin/init.mjs +236 -0
  4. package/package.json +47 -0
  5. package/payload/INSTALL.md +113 -0
  6. package/payload/agents/agent-architect.md +101 -0
  7. package/payload/agents/code-reviewer.md +87 -0
  8. package/payload/agents/codebase-auditor.md +73 -0
  9. package/payload/agents/coder.md +56 -0
  10. package/payload/agents/decomposer.md +70 -0
  11. package/payload/agents/docs-sync.md +115 -0
  12. package/payload/agents/e2e-test-writer.md +47 -0
  13. package/payload/agents/migration-reviewer.md +100 -0
  14. package/payload/agents/orchestrator.md +50 -0
  15. package/payload/agents/performance-reviewer.md +82 -0
  16. package/payload/agents/postmortem.md +83 -0
  17. package/payload/agents/release-mr.md +274 -0
  18. package/payload/agents/security-reviewer.md +122 -0
  19. package/payload/agents/test-fix.md +33 -0
  20. package/payload/agents/test-writer.md +40 -0
  21. package/payload/ci-templates/claude-pipeline.gitlab-ci.yml +233 -0
  22. package/payload/ci-templates/github/README.md +76 -0
  23. package/payload/ci-templates/github/claude-issue-pipeline.yml +141 -0
  24. package/payload/ci-templates/github/claude-pipeline.yml +141 -0
  25. package/payload/ci-templates/github/claude-test-fix.yml +104 -0
  26. package/payload/ci-templates/scripts/code.sh +114 -0
  27. package/payload/ci-templates/scripts/lib/issue-loop.sh +430 -0
  28. package/payload/ci-templates/scripts/lib/pipeline-common.sh +280 -0
  29. package/payload/ci-templates/scripts/lib/platform.sh +177 -0
  30. package/payload/ci-templates/scripts/lib/usage-capture.sh +110 -0
  31. package/payload/ci-templates/scripts/orchestrate.sh +294 -0
  32. package/payload/ci-templates/scripts/postmortem.sh +45 -0
  33. package/payload/ci-templates/scripts/review-fix.sh +90 -0
  34. package/payload/ci-templates/scripts/review.sh +93 -0
  35. package/payload/ci-templates/scripts/test-fix.sh +58 -0
  36. package/payload/skills/fix-review-findings/SKILL.md +79 -0
  37. package/payload/skills/fix-tests/SKILL.md +70 -0
  38. package/payload/skills/implement-issue/SKILL.md +62 -0
  39. package/payload/skills/init-pipeline-config/SKILL.md +96 -0
  40. package/payload/skills/postmortem-mr/SKILL.md +50 -0
  41. package/payload/skills/review-mr/SKILL.md +82 -0
  42. package/payload/skills/triage-issue/SKILL.md +74 -0
  43. package/payload/templates/pipeline-config.template.md +98 -0
  44. package/payload/templates/review_suppressions.template.md +25 -0
  45. package/payload/templates/spec-issue.template.md +64 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Technical University of Liberec
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,219 @@
1
+ # ci-agent-platform
2
+
3
+ > Autonomous dev pipeline on top of plain GitLab CI / GitHub Actions, driven by Claude Code.
4
+
5
+ The core is the **issue-to-code** loop: you file an issue and an open MR/PR comes
6
+ out. The orchestrator triages it, the coder implements it, and the review loop
7
+ checks the result. The input is a spec, the output is code. Details in
8
+ [docs/issue-to-code.md](https://gitlab.com/cxi-lmai/ci-agent-platform/-/blob/main/docs/issue-to-code.md).
9
+
10
+ - **15 generic agents**: triage, coding, review, tests, docs, release.
11
+ - **7 skills**, configured per project.
12
+ - **Project specifics live outside the agents**, in `.claude/pipeline-config.md` and `PIPE_*` variables.
13
+
14
+ > [!NOTE]
15
+ > The CI template wires two loops:
16
+ > - the **review loop** (`review`, `review-fix`, `test-fix`, and the shared escalation `/postmortem-mr`),
17
+ > - the **issue-to-code loop** (`orchestrate`, `code`, skills `triage-issue` and `implement-issue`).
18
+ >
19
+ > The other agents are installed too but have no CI job of their own. You invoke them by hand from the command line.
20
+
21
+ > [!CAUTION]
22
+ > The coder runs with `--dangerously-skip-permissions` and treats issue content
23
+ > as untrusted input, so it does not blindly implement anything from anyone. Run
24
+ > the loop only on maintainer-approved issues from trusted people, and on an
25
+ > isolated ephemeral runner.
26
+
27
+ ## Platform support
28
+
29
+ | Platform | Status | Tested |
30
+ | --- | --- | --- |
31
+ | GitLab | supported | end to end on a pilot project |
32
+ | GitHub | experimental | CI templates ship, the issue loop is unverified |
33
+
34
+ ## Contents
35
+
36
+ - [Platform support](#platform-support)
37
+ - [The full cycle](#the-full-cycle)
38
+ - [Repository layout](#repository-layout)
39
+ - [Install](#install)
40
+ - [What it costs](#what-it-costs)
41
+ - [Upgrading and removing](#upgrading-and-removing)
42
+ - [The issue-to-code loop (details)](https://gitlab.com/cxi-lmai/ci-agent-platform/-/blob/main/docs/issue-to-code.md)
43
+ - [How it fits together](#how-it-fits-together)
44
+ - [Possible extensions: changing agents and skills](#possible-extensions-changing-agents-and-skills)
45
+ - [Reference: secrets and variables](https://gitlab.com/cxi-lmai/ci-agent-platform/-/blob/main/docs/reference-variables.md)
46
+
47
+ ## The full cycle
48
+
49
+ <p align="center">
50
+ <img src="https://gitlab.com/cxi-lmai/ci-agent-platform/-/raw/main/docs/diagrams/full-cycle.svg" alt="The full cycle: from a labeled issue to a merge request ready to merge" width="760">
51
+ </p>
52
+
53
+ ## Repository layout
54
+
55
+ The package has two strictly separated halves. `bin/` is executable and never
56
+ reads or rewrites `payload/`; `payload/` is inert content that is copied and
57
+ never executed by Node. That separation is what lets an upgrade tell an
58
+ untouched file from one the project has edited.
59
+
60
+ ```
61
+ .
62
+ ├── LICENSE # MIT
63
+ ├── README.md # this file, and the npm front page
64
+ ├── package.json # bin: ci-agent-platform
65
+ ├── bin/init.mjs # phase 0: deterministic, no model
66
+ ├── payload/ # everything that ships into your repo
67
+ │ ├── INSTALL.md # instructions for Claude, copied to your root
68
+ │ ├── agents/ # 15 agent definitions (.md)
69
+ │ ├── skills/ # 7 skills (folder with a SKILL.md)
70
+ │ ├── templates/ # 3 templates: config, spec issue, suppressions
71
+ │ └── ci-templates/ # CI jobs and runner scripts (wired by the wizard)
72
+ │ ├── claude-pipeline.gitlab-ci.yml # GitLab CI template
73
+ │ ├── github/ # GitHub Actions workflows
74
+ │ └── scripts/ # shared runner scripts (both platforms)
75
+ ├── docs/ # supplementary documentation, not shipped
76
+ │ ├── diagrams/
77
+ │ ├── issue-to-code.md
78
+ │ ├── reference-variables.md
79
+ │ └── superpowers/ # this repository's own plans and specs
80
+ ├── examples/unitconv/ # a filled-in example config, not shipped
81
+ └── test/ # this repository's own tests, not shipped
82
+ ```
83
+
84
+ ## Install
85
+
86
+ Requires Node 20 or newer, git, and Claude Code. Run both from the root of the
87
+ repository you want to onboard:
88
+
89
+ ```bash
90
+ npx @cxi-lmai/ci-agent-platform # unpacks the platform
91
+ claude # then ask it to install the pipeline
92
+ ```
93
+
94
+ Two things are worth having before you start, though neither blocks the install.
95
+ A `CLAUDE.md` at the repository root: most agents read it as the project's
96
+ rulebook, and without one the reviewers work from `pipeline-config.md` alone. And
97
+ CI credentials for whichever platform you use, which the wizard walks you
98
+ through at the end. Any one of `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN` or
99
+ `CLAUDE_CODE_OAUTH_TOKEN` authenticates the agents, so a Claude subscription
100
+ works without buying API credit.
101
+
102
+ The first command is plain Node with no model in it. It refuses to run outside
103
+ a git repository, detects your platform from the git remote (without ever
104
+ printing the remote, which may embed a token), unpacks the payload into
105
+ `.ci-agent-platform-src/`, adds that plus two working paths to `.gitignore`,
106
+ writes `INSTALL.md` and a manifest of everything it wrote, and stops. It
107
+ commits nothing, pushes nothing, and asks for no secret.
108
+
109
+ The second hands over to Claude Code, which reads `INSTALL.md` and does the
110
+ rest:
111
+
112
+ 1. distributes the files into `.claude/` and `.claude-pipeline/`,
113
+ 2. scans the repository and generates `.claude/pipeline-config.md`,
114
+ 3. asks whether the issue-to-code loop should run manually or on a schedule,
115
+ and how often,
116
+ 4. wires your CI file (`.gitlab-ci.yml`, or a GitHub Actions workflow),
117
+ 5. walks you through the tokens one step at a time and verifies each one.
118
+
119
+ > [!NOTE]
120
+ > On a GitHub remote the bootstrapper stops and asks you to confirm that you
121
+ > accept experimental support before it writes anything. Pass
122
+ > `--experimental-github` to answer that in advance, or `--force` to proceed
123
+ > past a prior install it cannot account for.
124
+
125
+ ### What it costs
126
+
127
+ Every pipeline job spends paid Claude usage, so the defaults are deliberately
128
+ timid. The issue-to-code loop is **off** until you turn it on (`PIPE_ORCHESTRATE=1`
129
+ on a schedule or a manual run), and `PIPE_CODER_CAP` bounds how many issues one
130
+ orchestrate run hands to the coder, at 3. The review loop runs per merge
131
+ request, and `PIPE_FIX_LOOP_CAP` stops it after 2 bot fix commits rather than
132
+ letting it grind.
133
+
134
+ Turn the schedule on only once both smoke tests pass, and start with a slow
135
+ interval. A frequent schedule against a backlog of ready issues is the one
136
+ configuration that spends real money without anyone watching.
137
+
138
+ ### Upgrading and removing
139
+
140
+ Re-run `npx @cxi-lmai/ci-agent-platform` to take a newer release. It records what
141
+ it installed in `.claude/pipeline-install.json`, including a checksum per file,
142
+ so it can tell a file you edited from one it wrote.
143
+
144
+ > [!NOTE]
145
+ > Reconciling those checksums is not implemented yet. Until it is, re-running
146
+ > refreshes the source folder and the manifest; treat updating the installed
147
+ > copies under `.claude/` as a manual diff, and keep your own edits in
148
+ > `pipeline-config.md` rather than in agent files, where nothing will reclaim
149
+ > them.
150
+
151
+ To remove the pipeline: delete `.claude/agents/`, `.claude/skills/`,
152
+ `.claude/templates/`, `.claude/pipeline-config.md`,
153
+ `.claude/pipeline-install.json`, `.claude-pipeline/`, the spec issue template,
154
+ and `.ci-agent-platform-src/`; then drop the `include:` and the `PIPE_*`
155
+ variables from your CI file, and the three `# ci-agent-platform` lines from
156
+ `.gitignore`. Nothing else was touched.
157
+
158
+ > [!IMPORTANT]
159
+ > The installer **pushes only after you confirm** and **must not receive token
160
+ > values in the chat**. It asks permission before every push. You enter secret
161
+ > values yourself in the platform settings (CI/CD variables), so Claude never
162
+ > sees them in the conversation.
163
+
164
+ ## How it fits together
165
+
166
+ Each CI job runs `claude "/<skill>"`, and the work splits into two roles:
167
+
168
+ 1. **The skill** reads `$PIPE_CONFIG_PATH` and the context the runner prepared,
169
+ does the reasoning (build the diff, choose and run agents, verdict, classify),
170
+ and writes a marker file.
171
+ 2. **The runner** does only what needs a token or is mechanical: checkout,
172
+ prepare context from the API, compute the diff base, push commits, post
173
+ comments, set labels, emit metrics, gate.
174
+
175
+ Where to find what:
176
+
177
+ - **Skills** (what each takes as input, what it returns, which keys it writes to
178
+ the marker file): `payload/skills/` here, `.claude/skills/` once installed.
179
+ - **The CI jobs and runner scripts** behind them: `payload/ci-templates/` here,
180
+ `.claude-pipeline/` once installed.
181
+
182
+ ## Possible extensions: changing agents and skills
183
+
184
+ The platform is a kit. Adding a capability means adding one definition to your
185
+ own repository. Custom definitions live alongside the installed ones in
186
+ `.claude/`, and the installer never reclaims a file it did not write, so an
187
+ upgrade leaves them alone.
188
+
189
+ ### Agent
190
+
191
+ A single `.md` file in `.claude/agents/`. Its front matter
192
+ carries `name`, `description`, `tools`, and `model`, followed by the system
193
+ prompt. Platform convention: the prompt's first step reads `pipeline-config.md`
194
+ (path in `PIPE_CONFIG_PATH`) so the agent takes project specifics from there
195
+ rather than from hardcoded text. Agents are mostly read-only subagents that
196
+ skills call.
197
+
198
+ ### Skill
199
+
200
+ A folder with a `SKILL.md` file in `.claude/skills/`. A CI job
201
+ runs it as `claude "/<skill-name>"`. The skill holds the reasoning: it reads the
202
+ config and the prepared context, picks and runs agents, writes the result, and
203
+ emits a marker file for the runner.
204
+
205
+ ### How to add one
206
+
207
+ 1. Create the definition (`.claude/agents/<name>.md` or
208
+ `.claude/skills/<name>/SKILL.md`), using the installed ones as a template.
209
+ 2. Wire it in. A new agent is called by name from a skill (as a subagent), a new
210
+ skill needs a CI job that runs it (see `.claude-pipeline/`).
211
+ 3. Commit it. The CI runner does a clean checkout, so an uncommitted definition
212
+ does not exist as far as the pipeline is concerned.
213
+
214
+ > [!NOTE]
215
+ > One project's rules (conventions, domain checks) do not belong in custom
216
+ > agents, but in `pipeline-config.md`, the Domain Checks section, where every
217
+ > agent reads them. A custom agent is for a new capability, not for one
218
+ > repository's specifics. The `agent-architect` agent can propose improvements to
219
+ > the existing agents.
package/bin/init.mjs ADDED
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env node
2
+ // Phase 0 of the install: the deterministic half, with no model involved.
3
+ //
4
+ // It unpacks the payload, records what it wrote, and hands over to Claude Code
5
+ // for phases 1 to 6. It deliberately does the least it can: everything that
6
+ // needs judgement about the target repository belongs to the init skill, and
7
+ // everything that needs a secret belongs to the human.
8
+ //
9
+ // This file never reads or rewrites payload/. That separation is what lets the
10
+ // upgrade path treat payload files as content-addressable.
11
+
12
+ import { spawnSync } from 'node:child_process'
13
+ import { createHash } from 'node:crypto'
14
+ import fs from 'node:fs'
15
+ import path from 'node:path'
16
+ import readline from 'node:readline/promises'
17
+ import { fileURLToPath } from 'node:url'
18
+
19
+ const PKG_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
20
+ const PAYLOAD = path.join(PKG_ROOT, 'payload')
21
+ const SRC_DIR = '.ci-agent-platform-src'
22
+ const GITIGNORE_ENTRIES = [`${SRC_DIR}/`, '.claude/onboarding-state.md', 'build/pipeline/']
23
+
24
+ const pkg = JSON.parse(fs.readFileSync(path.join(PKG_ROOT, 'package.json'), 'utf8'))
25
+
26
+ const argv = process.argv.slice(2)
27
+ const has = (flag) => argv.includes(flag)
28
+ const FORCE = has('--force')
29
+ const YES_GITHUB = has('--experimental-github')
30
+
31
+ function die(message, hint) {
32
+ process.stderr.write(`ci-agent-platform: ${message}\n`)
33
+ if (hint) process.stderr.write(`\n${hint}\n`)
34
+ process.exit(1)
35
+ }
36
+
37
+ function say(message = '') {
38
+ process.stdout.write(`${message}\n`)
39
+ }
40
+
41
+ if (has('--help') || has('-h')) {
42
+ say(`ci-agent-platform ${pkg.version}
43
+
44
+ npx @cxi-lmai/ci-agent-platform unpack into the current repository
45
+ npx @cxi-lmai/ci-agent-platform --force proceed despite an ambiguous prior install
46
+
47
+ Options:
48
+ --experimental-github accept that GitHub support is experimental, without
49
+ being asked (for non-interactive use)
50
+ --version, --help
51
+
52
+ Phase 0 only. When it finishes, open Claude Code in this repository and ask it
53
+ to install the pipeline; it reads INSTALL.md and runs phases 1 to 6.`)
54
+ process.exit(0)
55
+ }
56
+
57
+ if (has('--version') || has('-v')) {
58
+ say(pkg.version)
59
+ process.exit(0)
60
+ }
61
+
62
+ // --- helpers ---------------------------------------------------------------
63
+
64
+ function git(...args) {
65
+ const r = spawnSync('git', args, { encoding: 'utf8' })
66
+ return { ok: r.status === 0, out: (r.stdout || '').trim() }
67
+ }
68
+
69
+ function sha256(file) {
70
+ return `sha256:${createHash('sha256').update(fs.readFileSync(file)).digest('hex')}`
71
+ }
72
+
73
+ // Payload-relative paths, sorted, so the manifest is stable across platforms.
74
+ function walk(dir, base = dir) {
75
+ return fs
76
+ .readdirSync(dir, { withFileTypes: true })
77
+ .flatMap((e) => {
78
+ const full = path.join(dir, e.name)
79
+ return e.isDirectory() ? walk(full, base) : [path.relative(base, full)]
80
+ })
81
+ .sort()
82
+ }
83
+
84
+ /**
85
+ * Classify the remote host without ever surfacing the URL. A remote can embed
86
+ * an access token in its userinfo, so the URL is read, matched, and dropped:
87
+ * it is never printed, never written to the state file, and never returned.
88
+ */
89
+ function detectPlatform() {
90
+ const r = git('remote', 'get-url', 'origin')
91
+ if (!r.ok || !r.out) return 'unknown'
92
+ const host = r.out.replace(/\/\/[^/@]+@/, '//')
93
+ if (/gitlab/i.test(host)) return 'gitlab'
94
+ if (/github/i.test(host)) return 'github'
95
+ return 'unknown'
96
+ }
97
+
98
+ async function confirm(question) {
99
+ if (YES_GITHUB) return true
100
+ if (!process.stdin.isTTY) return false
101
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout })
102
+ try {
103
+ const answer = await rl.question(`${question} [y/N] `)
104
+ return /^y(es)?$/i.test(answer.trim())
105
+ } finally {
106
+ rl.close()
107
+ }
108
+ }
109
+
110
+ function appendGitignore(repoRoot) {
111
+ const file = path.join(repoRoot, '.gitignore')
112
+ const existing = fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : ''
113
+ const lines = existing.split('\n').map((l) => l.trim())
114
+ const missing = GITIGNORE_ENTRIES.filter((e) => !lines.includes(e))
115
+ if (missing.length === 0) return []
116
+ const prefix = existing === '' || existing.endsWith('\n') ? '' : '\n'
117
+ fs.appendFileSync(file, `${prefix}\n# ci-agent-platform\n${missing.join('\n')}\n`)
118
+ return missing
119
+ }
120
+
121
+ // --- preflight -------------------------------------------------------------
122
+
123
+ const top = git('rev-parse', '--show-toplevel')
124
+ if (!top.ok) {
125
+ die(
126
+ 'this is not a git repository.',
127
+ 'Run it from the root of the repository you want to onboard:\n' +
128
+ ' cd /path/to/your/repo && npx @cxi-lmai/ci-agent-platform',
129
+ )
130
+ }
131
+ const REPO = top.out
132
+ process.chdir(REPO)
133
+
134
+ const configPath = path.join(REPO, '.claude', 'pipeline-config.md')
135
+ const manifestPath = path.join(REPO, '.claude', 'pipeline-install.json')
136
+ if (fs.existsSync(configPath) && !fs.existsSync(manifestPath) && !FORCE) {
137
+ die(
138
+ 'found .claude/pipeline-config.md but no .claude/pipeline-install.json.',
139
+ 'That combination means an earlier install this tool cannot account for, so\n' +
140
+ 'it will not overwrite anything. Re-run with --force once you are satisfied\n' +
141
+ 'the existing config can be reconciled by hand.',
142
+ )
143
+ }
144
+
145
+ const platform = detectPlatform()
146
+ let githubOptIn = null
147
+ if (platform === 'github') {
148
+ say('GitHub support is experimental.')
149
+ say('The CI workflows ship and are manually verified, but the issue-to-code')
150
+ say('loop carries GitLab-shaped assumptions and is unverified on GitHub.')
151
+ say('GitLab is the supported and tested platform.')
152
+ say()
153
+ githubOptIn = await confirm('Continue anyway?')
154
+ if (!githubOptIn) {
155
+ die(
156
+ 'stopped at the GitHub experimental-support check.',
157
+ 'Re-run with --experimental-github to accept this without being asked.',
158
+ )
159
+ }
160
+ }
161
+
162
+ // --- write -----------------------------------------------------------------
163
+
164
+ const srcPath = path.join(REPO, SRC_DIR)
165
+ fs.rmSync(srcPath, { recursive: true, force: true })
166
+ fs.cpSync(PAYLOAD, srcPath, { recursive: true })
167
+
168
+ const addedIgnores = appendGitignore(REPO)
169
+
170
+ // INSTALL.md is copied verbatim: it holds no per-project fact, so there is
171
+ // nothing to template. It is NOT copied over a consumer's own INSTALL.md,
172
+ // which is exactly the class of defect this project shipped once already.
173
+ let installName = 'INSTALL.md'
174
+ const payloadInstall = path.join(PAYLOAD, 'INSTALL.md')
175
+ const rootInstall = path.join(REPO, installName)
176
+ if (fs.existsSync(rootInstall)) {
177
+ const same = fs.readFileSync(rootInstall, 'utf8') === fs.readFileSync(payloadInstall, 'utf8')
178
+ if (!same) installName = 'INSTALL.ci-agent-platform.md'
179
+ }
180
+ fs.copyFileSync(payloadInstall, path.join(REPO, installName))
181
+
182
+ fs.mkdirSync(path.join(REPO, '.claude'), { recursive: true })
183
+
184
+ const payloadFiles = walk(PAYLOAD)
185
+ const manifest = {
186
+ package: pkg.name,
187
+ version: pkg.version,
188
+ platform,
189
+ payload: Object.fromEntries(payloadFiles.map((rel) => [rel, sha256(path.join(PAYLOAD, rel))])),
190
+ }
191
+ fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`)
192
+
193
+ fs.writeFileSync(
194
+ path.join(REPO, '.claude', 'onboarding-state.md'),
195
+ `# Onboarding state
196
+
197
+ Phase: 0-complete
198
+ Platform: ${platform}
199
+ GitHub experimental opt-in: ${githubOptIn === null ? 'not applicable' : githubOptIn}
200
+ Source folder: ${SRC_DIR}/
201
+ Install instructions: ${installName}
202
+
203
+ Next action: phase 1 of .ci-agent-platform-src/skills/init-pipeline-config/SKILL.md
204
+ (scan the repository, select agents, draft the config).
205
+
206
+ This file is gitignored and is deleted at the end of the install. A session
207
+ resuming an interrupted onboarding reads it first and continues from the phase
208
+ recorded here, following the skill rather than improvising.
209
+ `,
210
+ )
211
+
212
+ // --- handoff ---------------------------------------------------------------
213
+
214
+ say()
215
+ say(`ci-agent-platform ${pkg.version}: phase 0 complete.`)
216
+ say()
217
+ say(` unpacked ${SRC_DIR}/ (${payloadFiles.length} files)`)
218
+ say(` wrote ${installName}`)
219
+ say(' .claude/pipeline-install.json')
220
+ say(' .claude/onboarding-state.md')
221
+ say(` gitignored ${addedIgnores.length > 0 ? addedIgnores.join(', ') : 'nothing new'}`)
222
+ say(` platform ${platform}${platform === 'github' ? ' (experimental)' : ''}`)
223
+ if (installName !== 'INSTALL.md') {
224
+ say()
225
+ say(' Note: this repository already had its own INSTALL.md, which was left')
226
+ say(` untouched. The install instructions went to ${installName}.`)
227
+ }
228
+ if (platform === 'unknown') {
229
+ say()
230
+ say(' Note: no git remote found, so the platform is unknown. Phase 1 asks.')
231
+ }
232
+ say()
233
+ say('Next: open Claude Code in this repository and ask it to install the')
234
+ say('pipeline (any phrasing). It reads the instructions and takes it from here.')
235
+ say()
236
+ say('Nothing has been committed. Nothing has been pushed.')
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@cxi-lmai/ci-agent-platform",
3
+ "version": "3.0.0",
4
+ "description": "Autonomous dev pipeline on plain GitLab CI or GitHub Actions, driven by Claude Code. A labeled issue goes in, an open merge request comes out.",
5
+ "keywords": [
6
+ "claude",
7
+ "claude-code",
8
+ "ci",
9
+ "gitlab-ci",
10
+ "github-actions",
11
+ "agents",
12
+ "code-review",
13
+ "automation"
14
+ ],
15
+ "homepage": "https://gitlab.com/cxi-lmai/ci-agent-platform#readme",
16
+ "bugs": {
17
+ "url": "https://gitlab.com/cxi-lmai/ci-agent-platform/-/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://gitlab.com/cxi-lmai/ci-agent-platform.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Technical University of Liberec",
25
+ "type": "module",
26
+ "bin": {
27
+ "ci-agent-platform": "bin/init.mjs"
28
+ },
29
+ "files": [
30
+ "bin/",
31
+ "payload/",
32
+ "README.md",
33
+ "LICENSE"
34
+ ],
35
+ "engines": {
36
+ "node": ">=20"
37
+ },
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "scripts": {
42
+ "test": "npm run test:lint && npm run test:gates && npm run test:unit",
43
+ "test:lint": "shellcheck -S warning test/*.sh payload/ci-templates/scripts/*.sh payload/ci-templates/scripts/lib/*.sh",
44
+ "test:gates": "bash test/coherence-gate.sh all && bash test/restructure-invariants.sh verify",
45
+ "test:unit": "node --test \"test/*.test.mjs\""
46
+ }
47
+ }
@@ -0,0 +1,113 @@
1
+ # ci-agent-platform: onboarding
2
+
3
+ Instructions for Claude Code. `npx @cxi-lmai/ci-agent-platform` unpacked the
4
+ platform into this repository, and the user has asked to install it (any
5
+ phrasing: "install ci-agent-platform", "install the pipeline", "set this up").
6
+ Follow this file top to bottom. You are the installer and the guide; the user
7
+ answers questions and approves steps.
8
+
9
+ Ground rules:
10
+
11
+ - Never push and never commit without showing what changed and asking first.
12
+ - Never accept secret values (API keys, tokens) into the conversation. Name
13
+ them, say where to set them, verify presence, not values.
14
+ - Do not overwrite existing files without showing a diff and asking.
15
+ - `.ci-agent-platform-src/` is the source. It is gitignored, so everything the
16
+ CI needs must be copied into tracked paths (step 1): the runner does a clean
17
+ checkout and sees only what the repository tracks.
18
+
19
+ ## Step 0: check that the bootstrap ran
20
+
21
+ `.ci-agent-platform-src/` and `.claude/pipeline-install.json` both exist once
22
+ the bootstrapper has run. If either is missing, the user has not run it. Ask
23
+ them to run `npx @cxi-lmai/ci-agent-platform` in this repository, and stop. Do
24
+ not clone the platform yourself and do not improvise a source folder: the
25
+ manifest that the upgrade path depends on is written only by the bootstrapper.
26
+
27
+ Then read `.claude/onboarding-state.md`, which records the detected platform
28
+ and the phase reached.
29
+
30
+ ## Step 1: distribute the files
31
+
32
+ Let `<src>` be `.ci-agent-platform-src/`.
33
+
34
+ The platform is `gitlab` or `github`, and `.claude/onboarding-state.md` already
35
+ records which. Use that value. Do not re-read the git remote: the bootstrapper
36
+ detected the platform precisely so that a URL which may embed an access token
37
+ never has to enter this conversation.
38
+
39
+ 1. Copy `<src>/agents/` to `.claude/agents/` and `<src>/skills/` to
40
+ `.claude/skills/` (tracked: the CI runner does a clean checkout and needs
41
+ them in the repo).
42
+
43
+ **Never overwrite a file that is already there.** Repositories that qualify
44
+ to install this pipeline are exactly the ones likely to have their own
45
+ `.claude/agents/code-reviewer.md` or `coder.md` already, tuned by hand. For
46
+ every name that collides, show the diff and ask, one file at a time. Do not
47
+ offer "overwrite all". A definition the project wrote outranks the one that
48
+ ships here, and losing it silently is the worst outcome of the whole install.
49
+ 2. Copy `<src>/templates/` to `.claude/templates/`, under the same rule.
50
+ 3. Create `.claude/memory/review_suppressions.md` from
51
+ `<src>/templates/review_suppressions.template.md` **when it does not already
52
+ exist**, and never touch it when it does: it is a project-owned record.
53
+ The reviewer agents read it on every run, and without the file they read a
54
+ missing path on a repository that has just been told the pipeline is
55
+ installed.
56
+ 4. Copy the CI template and runner scripts. The runner scripts go to the same
57
+ place on both platforms, `.claude-pipeline/scripts/`, which is the default
58
+ `PIPE_SCRIPTS_DIR` the CI template already points at. Only the CI definition
59
+ differs:
60
+ - GitLab: `<src>/ci-templates/claude-pipeline.gitlab-ci.yml` to
61
+ `.claude-pipeline/`, and `<src>/ci-templates/scripts/` to
62
+ `.claude-pipeline/scripts/`.
63
+ - GitHub: `<src>/ci-templates/github/*.yml` to `.github/workflows/`, and
64
+ `<src>/ci-templates/scripts/` to `.claude-pipeline/scripts/`. Ask before
65
+ replacing a workflow file that already exists; `claude-pipeline.yml` is an
66
+ ordinary enough name to collide.
67
+ 5. Check `.gitignore`. The bootstrapper already added `.ci-agent-platform-src/`,
68
+ `.claude/onboarding-state.md` (the wizard's progress marker, see the skill's
69
+ ground rules) and `build/pipeline/` (the default `PIPE_CONTEXT_DIR`, the
70
+ runner's working files). Add any that are missing. The source folder is a
71
+ copy of the distributed package; the installed files above are what the
72
+ project tracks.
73
+
74
+ Do not commit yet; one commit covers install and configuration together at
75
+ the end of step 2.
76
+
77
+ When you reach that commit, do not stage the copy of this file at the
78
+ repository root (`INSTALL.md`, or `INSTALL.ci-agent-platform.md` where the
79
+ project had its own). It is an instruction sheet for you, not documentation for
80
+ the project, and it reads as the latter once committed. Delete it together with
81
+ `.ci-agent-platform-src/` at the end of the install, as you already do with
82
+ `.claude/onboarding-state.md`. Everything either file was needed for is by then
83
+ recorded in `.claude/pipeline-install.json`, and re-running the bootstrapper
84
+ restores both.
85
+
86
+ ## Step 2: run the configuration wizard
87
+
88
+ Follow `.claude/skills/init-pipeline-config/SKILL.md` from phase 1 (it is now
89
+ in the repo; in a fresh session it is also available as the
90
+ `/init-pipeline-config` skill). It scans the repo, generates
91
+ `.claude/pipeline-config.md`, asks the user only what it cannot detect, wires
92
+ the project CI file, guides the secrets setup one step at a time (with a
93
+ step counter), runs the smoke test itself, and closes with one recap table
94
+ of every decision.
95
+
96
+ Do not duplicate its content here. One rule binds both files: the user does
97
+ only what a human must (tokens, CI variables), you do everything else. Keep
98
+ the ceremony minimal: no per-step approvals, one commit at the end. Ask only
99
+ when something is destructive, secret-related, or about to be pushed.
100
+
101
+ Once running, a ready issue must carry its full spec in the issue description,
102
+ not in a comment: the orchestrator reads only the description, and a spec left
103
+ in a comment triages as missing sections.
104
+
105
+ ## Resuming an interrupted onboarding
106
+
107
+ When a new session is asked to continue an installation ("continue", "resume",
108
+ any phrasing, in any language), do not improvise from the repo state. Read
109
+ `.claude/onboarding-state.md` (the wizard maintains it), then re-read
110
+ `.claude/skills/init-pipeline-config/SKILL.md`, and continue at the recorded
111
+ phase, following the skill to the letter. Completed phases are never redone.
112
+ When the state file is missing, infer the phase from the repo (installed
113
+ files, config, git log) and still follow the skill, not your own plan.