agent-orchestrator-kit 0.1.4 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.1.6] - 2026-07-02
6
+
7
+ ### Added
8
+ - **`init --spec-verify`** — opt-in AI Spec Verifier for GitLab consumers: on MRs changing `src/`, an Amp agent verifies code against `openspec/specs/`, posts PASS/BLOCKED to the MR, and fails the pipeline on BLOCKED
9
+ - **Templates** — `.gitlab/spec-verify.yml` (blocking job, commented Phase 1 `allow_failure` fallback), `scripts/verify-specs.sh` (stack-agnostic prompt with project context from `openspec/config.yaml`, graceful skips, secret-safe), `scripts/post-mr-verdict.sh` (GitLab MR comment)
10
+ - **Orchestrator gate** — `spec-verify-blocking` auto-added to `roles.verifier.gates` (idempotent)
11
+ - **OpenSpec spec** — `spec-verify-consumer`
12
+
13
+ ### Changed
14
+ - **`update`** refreshes spec-verify files via `KIT_OPTIN_PATHS` — only in projects that already installed them
15
+ - **README / AGENTS.md template** — AI Spec Verifier documented (install, CI variables, verdict schema, Phase 1 rollout)
16
+
17
+ ## [0.1.5] - 2026-06-27
18
+
19
+ ### Added
20
+ - **`init --ci gitlab|github|none`** — CI provider flag (default: `github`)
21
+ - **GitLab verify** — `.gitlab/agent-verify.yml` fragment with multi-PM detect (npm/yarn/pnpm)
22
+ - **PM-aware prebuild hook** — `verify:openspec` + `prebuild` injection on `--ci gitlab` (zero DevOps config via `npm run build`)
23
+ - **Starter example** — `templates/.gitlab-ci.starter.yml.example` for early push before DevOps owns root CI
24
+ - **OpenSpec specs** — `gitlab-consumer-verify`, `kit-ci-verify` synced to `openspec/specs/`
25
+
26
+ ### Changed
27
+ - **`update`** refreshes `.gitlab/agent-verify.yml` via `KIT_MANAGED_PATHS`
28
+ - **README + AGENTS.md** — GitLab verifier path documented (prebuild hook, not GitHub Actions)
29
+ - **`printNextSteps`** — GitLab hints for `--ci gitlab` users
30
+
5
31
  ## [0.1.4] - 2026-06-27
6
32
 
7
33
  ### Added
@@ -49,6 +75,7 @@ All notable changes to this project will be documented in this file.
49
75
  ### Added
50
76
  - Initial release: 5-role orchestration pipeline, `/opsx:*` commands, IDE sync
51
77
 
78
+ [0.1.5]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.1.4...v0.1.5
52
79
  [0.1.4]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.1.3...v0.1.4
53
80
  [0.1.3]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.1.2...v0.1.3
54
81
  [0.1.2]: https://github.com/makshc2/agent-orchestrator-kit/compare/v0.1.1...v0.1.2
package/README.md CHANGED
@@ -64,6 +64,24 @@ npx agent-orchestrator-kit init \
64
64
  --lang uk
65
65
  ```
66
66
 
67
+ For GitLab-hosted projects (verify via `prebuild` hook — no GitHub Actions):
68
+
69
+ ```bash
70
+ npx agent-orchestrator-kit init --ci gitlab
71
+ ```
72
+
73
+ This installs `.gitlab/agent-verify.yml`, injects `verify:openspec` and PM-aware `prebuild` into `package.json`. When DevOps runs `npm run build` (or yarn/pnpm build), npm lifecycle runs `prebuild` first → `npx openspec validate --all --strict` executes automatically.
74
+
75
+ Optional dev-controlled CI before DevOps setup: copy `templates/.gitlab-ci.starter.yml.example` from the kit to `.gitlab-ci.yml` and adjust stages as needed.
76
+
77
+ Skip CI files entirely:
78
+
79
+ ```bash
80
+ npx agent-orchestrator-kit init --ci none
81
+ ```
82
+
83
+ Default remains GitHub Actions (`--ci github`).
84
+
67
85
  ### Sync to local IDEs
68
86
 
69
87
  After init (and after every update):
@@ -80,7 +98,9 @@ This copies `.agents/` to your local IDE directories (not committed to git).
80
98
  your-project/
81
99
  ├── AGENTS.md
82
100
  ├── CLAUDE.md
83
- ├── .github/workflows/agent-verify.yml # CI: openspec validate + lint + build
101
+ ├── .github/workflows/agent-verify.yml # CI (default --ci github)
102
+ ├── .gitlab/agent-verify.yml # CI fragment (--ci gitlab)
103
+ ├── .gitlab/spec-verify.yml # AI Spec Verifier (--spec-verify, opt-in)
84
104
  ├── .agents/
85
105
  │ ├── orchestrator.yaml
86
106
  │ ├── mcp.json.example # Cursor MCP template
@@ -96,7 +116,8 @@ your-project/
96
116
  │ ├── openspec-archive-change/
97
117
  │ ├── openspec-sync-specs/
98
118
  │ └── spec-workflow-openspec/
99
- └── scripts/sync-local-agent-skills.sh
119
+ ├── scripts/sync-local-agent-skills.sh
120
+ └── scripts/verify-specs.sh + post-mr-verdict.sh # (--spec-verify, opt-in)
100
121
  ```
101
122
 
102
123
  ### Included in kit
@@ -106,7 +127,8 @@ your-project/
106
127
  | Orchestration | 5-role pipeline, `AGENTS.md`, `orchestrator.yaml`, review command |
107
128
  | OpenSpec skills | All 7 skills for `/opsx:*` workflow |
108
129
  | IDE sync | Cursor + Claude Code sync script |
109
- | CI | `agent-verify.yml` (openspec validate, lint, build, test) |
130
+ | CI | `agent-verify.yml` — GitHub (default) or GitLab fragment + `prebuild` hook |
131
+ | AI Spec Verifier | `spec-verify.yml` + verifier scripts — GitLab opt-in (`--spec-verify`) |
110
132
  | MCP templates | Memory MCP for Cursor and Amp |
111
133
 
112
134
  ### Not included (install separately)
@@ -285,7 +307,7 @@ npm run lint # must pass
285
307
 
286
308
  ### Role 5: Verifier — CI (automatic)
287
309
 
288
- Installed at `.github/workflows/agent-verify.yml`:
310
+ **GitHub (default `--ci github`):** installed at `.github/workflows/agent-verify.yml`:
289
311
 
290
312
  ```yaml
291
313
  - run: npx openspec validate --all --strict
@@ -294,8 +316,54 @@ Installed at `.github/workflows/agent-verify.yml`:
294
316
  - run: npm test --if-present
295
317
  ```
296
318
 
319
+ **GitLab (`--ci gitlab`):** verify runs through the package manager build lifecycle — no GitHub Actions:
320
+
321
+ ```json
322
+ "verify:openspec": "npx openspec validate --all --strict",
323
+ "prebuild": "npm run verify:openspec"
324
+ ```
325
+
326
+ When CI or a developer runs `npm run build`, npm executes `prebuild` first. DevOps pipelines that already call `npm run build` get OpenSpec validate with zero config changes.
327
+
328
+ Optional: include `.gitlab/agent-verify.yml` in `.gitlab-ci.yml` for full lint/build/test verify before DevOps owns the root CI file. See kit `templates/.gitlab-ci.starter.yml.example`.
329
+
297
330
  Blocks merge if any gate fails.
298
331
 
332
+ #### AI Spec Verifier (GitLab, opt-in)
333
+
334
+ ```bash
335
+ npx agent-orchestrator-kit init --ci gitlab --spec-verify
336
+ ```
337
+
338
+ Installs an AI verification layer on top of the deterministic gates: on every merge request that changes `src/`, an Amp agent reads `openspec/specs/`, checks the changed code against every relevant requirement, posts a **PASS / BLOCKED** comment to the MR, and **fails the pipeline on BLOCKED** — specs become an enforceable merge contract, not just documentation.
339
+
340
+ Installed files:
341
+
342
+ | File | Purpose |
343
+ |------|---------|
344
+ | `.gitlab/spec-verify.yml` | CI fragment — hidden `.spec-verify-base` + blocking `spec-verify` job (MR + `src/**/*` only) |
345
+ | `scripts/verify-specs.sh` | Collects changed files + specs, builds prompt (project context from `openspec/config.yaml`), calls `amp -x`, writes `artifacts/verdict.json` |
346
+ | `scripts/post-mr-verdict.sh` | Posts the verdict as an MR comment via GitLab API |
347
+
348
+ The flag also adds `spec-verify-blocking` to `roles.verifier.gates` in `.agents/orchestrator.yaml`.
349
+
350
+ Setup after install:
351
+
352
+ 1. Include the fragment from `.gitlab-ci.yml`:
353
+
354
+ ```yaml
355
+ include:
356
+ - local: '.gitlab/spec-verify.yml'
357
+ ```
358
+
359
+ 2. Add CI/CD variables (Settings → CI/CD → Variables, masked): `AMP_API_KEY`, `GITLAB_VERIFIER_TOKEN` (project access token with `api` scope).
360
+
361
+ Verdict schema (`artifacts/verdict.json`): `pass`, `score` (0–100), `summary`, `findings[]` with `severity` (`error` fails the job), `spec`, `requirement`, `message`, `file`. The script degrades gracefully — no `src/` changes, no specs, missing `amp` CLI, or missing `AMP_API_KEY` produce a skipped passing verdict and never block the pipeline. Secrets are never logged; `.env`/key/token files are excluded from prompts.
362
+
363
+ **Warning-only rollout (Phase 1):** uncomment `allow_failure: true` in `.gitlab/spec-verify.yml` to keep the pipeline green while the team builds trust in verdicts, then remove it to enforce blocking (Phase 2).
364
+
365
+ `update` refreshes the three spec-verify files only in projects that already installed them — the feature stays opt-in.
366
+
299
367
  ---
300
368
 
301
369
  ### Archive — `/opsx:archive`
@@ -429,6 +497,8 @@ npx agent-orchestrator-kit init [options]
429
497
  --profile <name> Stack profile: generic | vue3 | node | mvp
430
498
  --lang <code> Agent language: en | uk | ...
431
499
  --name <name> Project name (default: directory name)
500
+ --ci <provider> CI provider: gitlab | github | none (default: github)
501
+ --spec-verify Install AI Spec Verifier blocking gate (GitLab only)
432
502
  --force Overwrite existing files
433
503
 
434
504
  npx agent-orchestrator-kit update
@@ -470,6 +540,22 @@ openspec/ # Committed — spec-driven workflow
470
540
 
471
541
  ## Changelog
472
542
 
543
+ ### 0.1.6
544
+ - `init --ci gitlab --spec-verify` — opt-in AI Spec Verifier: blocking MR gate via Amp CLI
545
+ - Templates: `.gitlab/spec-verify.yml`, `scripts/verify-specs.sh`, `scripts/post-mr-verdict.sh`
546
+ - `spec-verify-blocking` gate auto-added to `roles.verifier.gates`
547
+ - `update` refreshes spec-verify files only where already installed
548
+
549
+ ### 0.1.5
550
+ - `init --ci gitlab|github|none` — GitLab verify via prebuild hook + CI fragment
551
+ - PM-aware `verify:openspec` / `prebuild` injection for GitLab projects
552
+ - `.gitlab/agent-verify.yml` template + starter example
553
+ - `update` refreshes GitLab fragment; docs for GitLab verifier path
554
+
555
+ ### 0.1.4
556
+ - Kit repo CI — `.github/workflows/agent-verify.yml`
557
+ - OpenSpec devDependency for local and CI validation
558
+
473
559
  ### 0.1.3
474
560
  - Fix gitignore dedup (exact line match, not substring)
475
561
  - Add `.claude` to gitignore on init
@@ -28,9 +28,20 @@ const KIT_MANAGED_PATHS = [
28
28
  '.agents/rules',
29
29
  ...KIT_SKILL_DIRS.map((s) => `.agents/skills/${s}`),
30
30
  '.github/workflows/agent-verify.yml',
31
+ '.gitlab/agent-verify.yml',
31
32
  'scripts/sync-local-agent-skills.sh',
32
33
  ];
33
34
 
35
+ // Opt-in files: refreshed by `update` only when already present in the project
36
+ const KIT_OPTIN_PATHS = [
37
+ '.gitlab/spec-verify.yml',
38
+ 'scripts/verify-specs.sh',
39
+ 'scripts/post-mr-verdict.sh',
40
+ ];
41
+
42
+ const VALID_CI_PROVIDERS = ['gitlab', 'github', 'none'];
43
+ const VERIFY_OPENSPEC_SCRIPT = 'npx openspec validate --all --strict';
44
+
34
45
  const GITIGNORE_LINES = ['.cursor', '.cursor/memory.json', '.amp/settings.json', '.claude'];
35
46
 
36
47
  const log = {
@@ -146,6 +157,114 @@ function installOpenspecConfigExample(projectDir, profile, vars, force) {
146
157
  }
147
158
  }
148
159
 
160
+ function resolveCiProvider(ci) {
161
+ if (VALID_CI_PROVIDERS.includes(ci)) return ci;
162
+ log.warn(`Unknown --ci value "${ci}". Valid: ${VALID_CI_PROVIDERS.join(', ')}. Using github.`);
163
+ return 'github';
164
+ }
165
+
166
+ function injectVerifyScripts(projectDir, { pm }) {
167
+ const pkgPath = join(projectDir, 'package.json');
168
+ if (!existsSync(pkgPath)) {
169
+ log.warn('skip script injection: no package.json');
170
+ return;
171
+ }
172
+
173
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
174
+ if (!pkg.scripts) pkg.scripts = {};
175
+
176
+ if (pkg.scripts['verify:openspec']) {
177
+ log.warn('skip (exists): verify:openspec script');
178
+ } else {
179
+ pkg.scripts['verify:openspec'] = VERIFY_OPENSPEC_SCRIPT;
180
+ log.ok('verify:openspec script added');
181
+ }
182
+
183
+ const runCmd = `${pm} run verify:openspec`;
184
+ const existingPrebuild = pkg.scripts.prebuild;
185
+
186
+ if (existingPrebuild && existingPrebuild.includes('verify:openspec')) {
187
+ log.warn('skip (exists): prebuild already chains verify:openspec');
188
+ } else if (existingPrebuild) {
189
+ pkg.scripts.prebuild = `${runCmd} && ${existingPrebuild}`;
190
+ log.ok('prebuild script chained');
191
+ } else {
192
+ pkg.scripts.prebuild = runCmd;
193
+ log.ok('prebuild script added');
194
+ }
195
+
196
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
197
+ }
198
+
199
+ function installCi(projectDir, templateDir, ci, force) {
200
+ if (ci === 'none') {
201
+ log.info('CI install skipped (--ci none)');
202
+ return;
203
+ }
204
+
205
+ if (ci === 'github') {
206
+ const githubWorkflow = join(templateDir, '.github', 'workflows', 'agent-verify.yml');
207
+ const githubDest = join(projectDir, '.github', 'workflows', 'agent-verify.yml');
208
+ if (!existsSync(githubWorkflow)) return;
209
+ if (!force && existsSync(githubDest)) {
210
+ log.warn('skip (exists): .github/workflows/agent-verify.yml');
211
+ return;
212
+ }
213
+ mkdirSync(dirname(githubDest), { recursive: true });
214
+ copyFileSync(githubWorkflow, githubDest);
215
+ log.ok('.github/workflows/agent-verify.yml');
216
+ return;
217
+ }
218
+
219
+ if (ci === 'gitlab') {
220
+ const gitlabFragment = join(templateDir, '.gitlab', 'agent-verify.yml');
221
+ const gitlabDest = join(projectDir, '.gitlab', 'agent-verify.yml');
222
+ if (!existsSync(gitlabFragment)) return;
223
+ if (!force && existsSync(gitlabDest)) {
224
+ log.warn('skip (exists): .gitlab/agent-verify.yml');
225
+ return;
226
+ }
227
+ mkdirSync(dirname(gitlabDest), { recursive: true });
228
+ copyFileSync(gitlabFragment, gitlabDest);
229
+ log.ok('.gitlab/agent-verify.yml');
230
+ }
231
+ }
232
+
233
+ function installSpecVerify(projectDir, templateDir, force) {
234
+ for (const rel of KIT_OPTIN_PATHS) {
235
+ const src = join(templateDir, rel);
236
+ const dest = join(projectDir, rel);
237
+ if (!existsSync(src)) continue;
238
+ if (!force && existsSync(dest)) {
239
+ log.warn(`skip (exists): ${rel}`);
240
+ continue;
241
+ }
242
+ mkdirSync(dirname(dest), { recursive: true });
243
+ copyFileSync(src, dest);
244
+ log.ok(rel);
245
+ }
246
+ try {
247
+ execSync(`chmod +x ${join(projectDir, 'scripts', 'verify-specs.sh')} ${join(projectDir, 'scripts', 'post-mr-verdict.sh')}`);
248
+ } catch {}
249
+ }
250
+
251
+ function patchOrchestratorSpecVerify(projectDir) {
252
+ const orchPath = join(projectDir, '.agents', 'orchestrator.yaml');
253
+ if (!existsSync(orchPath)) return;
254
+
255
+ let content = readFileSync(orchPath, 'utf-8');
256
+ if (content.includes('spec-verify-blocking')) return;
257
+
258
+ const anchor = /^(\s*)- openspec-validate-strict\s*$/m;
259
+ if (!anchor.test(content)) {
260
+ log.warn('could not add spec-verify-blocking gate: openspec-validate-strict anchor not found in orchestrator.yaml');
261
+ return;
262
+ }
263
+ content = content.replace(anchor, '$1- openspec-validate-strict\n$1- spec-verify-blocking');
264
+ writeFileSync(orchPath, content);
265
+ log.ok('spec-verify-blocking gate added to orchestrator.yaml');
266
+ }
267
+
149
268
  function patchOrchestratorVerifier(projectDir, pm) {
150
269
  const orchPath = join(projectDir, '.agents', 'orchestrator.yaml');
151
270
  if (!existsSync(orchPath)) return;
@@ -168,7 +287,7 @@ function patchOrchestratorVerifier(projectDir, pm) {
168
287
  writeFileSync(orchPath, content);
169
288
  }
170
289
 
171
- function printNextSteps(profile, projectDir) {
290
+ function printNextSteps(profile, projectDir, ci = 'github', specVerify = false) {
172
291
  const pm = detectPackageManager(projectDir);
173
292
  const openspecReady = hasOpenSpec(projectDir);
174
293
  const lines = [`${pc.bold('Next steps:')}`];
@@ -206,6 +325,18 @@ function printNextSteps(profile, projectDir) {
206
325
  lines.push(` ${pc.dim(`Detected package manager: ${pm} (verifier commands updated in orchestrator.yaml)`)}`);
207
326
  }
208
327
 
328
+ if (ci === 'gitlab') {
329
+ lines.push(` ${pc.dim(`GitLab verify: ${pm} run build triggers prebuild → verify:openspec automatically`)}`);
330
+ lines.push(` ${pc.dim('Optional dev CI: include local .gitlab/agent-verify.yml (see kit templates/.gitlab-ci.starter.yml.example)')}`);
331
+ }
332
+
333
+ if (specVerify) {
334
+ lines.push(` ${pc.bold('AI Spec Verifier:')}`);
335
+ lines.push(` - include ${pc.cyan(".gitlab/spec-verify.yml")} from your .gitlab-ci.yml`);
336
+ lines.push(` - add CI/CD variables: ${pc.cyan('AMP_API_KEY')}, ${pc.cyan('GITLAB_VERIFIER_TOKEN')} (masked)`);
337
+ lines.push(` - BLOCKED verdict fails the MR pipeline (uncomment allow_failure for warning-only rollout)`);
338
+ }
339
+
209
340
  console.log('\n' + lines.join('\n') + '\n');
210
341
  }
211
342
 
@@ -236,17 +367,21 @@ program
236
367
  .option('--lang <lang>', 'Agent response language (en | uk | ...)', 'en')
237
368
  .option('--name <name>', 'Project name (defaults to directory name)')
238
369
  .option('--force', 'Overwrite existing files', false)
370
+ .option('--ci <provider>', 'CI provider: gitlab | github | none', 'github')
371
+ .option('--spec-verify', 'Install AI Spec Verifier blocking gate (GitLab only)', false)
239
372
  .action((opts) => {
240
373
  const projectDir = process.cwd();
241
374
  const projectName = opts.name || basename(projectDir);
242
375
  const profile = resolveProfile(opts.profile);
243
376
  const pm = detectPackageManager(projectDir);
377
+ const ci = resolveCiProvider(opts.ci);
244
378
 
245
379
  log.title(`agent-orchestrator init v${KIT_VERSION}`);
246
380
  log.info(`Project: ${projectName}`);
247
381
  log.info(`Profile: ${profile}`);
248
382
  log.info(`Language: ${opts.lang}`);
249
383
  log.info(`Package manager: ${pm}`);
384
+ log.info(`CI provider: ${ci}`);
250
385
 
251
386
  const templateDir = join(KIT_ROOT, 'templates');
252
387
  const profileDir = join(KIT_ROOT, 'profiles', profile);
@@ -259,22 +394,27 @@ program
259
394
  }
260
395
 
261
396
  log.title('Installing scripts/');
262
- copyDir(join(templateDir, 'scripts'), join(projectDir, 'scripts'), { overwrite: opts.force });
397
+ copyDir(join(templateDir, 'scripts'), join(projectDir, 'scripts'), {
398
+ overwrite: opts.force,
399
+ skip: ['verify-specs.sh', 'post-mr-verdict.sh'],
400
+ });
263
401
  try {
264
402
  execSync(`chmod +x ${join(projectDir, 'scripts', 'sync-local-agent-skills.sh')}`);
265
403
  } catch {}
266
404
 
267
405
  log.title('Installing CI workflow');
268
- const githubWorkflow = join(templateDir, '.github', 'workflows', 'agent-verify.yml');
269
- const githubDest = join(projectDir, '.github', 'workflows', 'agent-verify.yml');
270
- if (existsSync(githubWorkflow)) {
271
- if (!opts.force && existsSync(githubDest)) {
272
- log.warn('skip (exists): .github/workflows/agent-verify.yml');
273
- } else {
274
- mkdirSync(dirname(githubDest), { recursive: true });
275
- copyFileSync(githubWorkflow, githubDest);
276
- log.ok('.github/workflows/agent-verify.yml');
277
- }
406
+ installCi(projectDir, templateDir, ci, opts.force);
407
+ if (ci === 'gitlab') {
408
+ injectVerifyScripts(projectDir, { pm });
409
+ }
410
+
411
+ const specVerify = Boolean(opts.specVerify) && ci === 'gitlab';
412
+ if (opts.specVerify && ci !== 'gitlab') {
413
+ log.warn('--spec-verify requires --ci gitlab — skipping AI Spec Verifier install');
414
+ }
415
+ if (specVerify) {
416
+ log.title('Installing AI Spec Verifier (opt-in)');
417
+ installSpecVerify(projectDir, templateDir, opts.force);
278
418
  }
279
419
 
280
420
  log.title('Installing root files');
@@ -302,6 +442,9 @@ program
302
442
  patchOrchestratorVerifier(projectDir, pm);
303
443
  log.ok('.agents/orchestrator.yaml');
304
444
  }
445
+ if (specVerify) {
446
+ patchOrchestratorSpecVerify(projectDir);
447
+ }
305
448
 
306
449
  log.title('OpenSpec config template');
307
450
  installOpenspecConfigExample(projectDir, profile, vars, opts.force);
@@ -311,7 +454,7 @@ program
311
454
 
312
455
  log.title('Done');
313
456
  log.ok(`agent-orchestrator-kit v${KIT_VERSION} installed`);
314
- printNextSteps(profile, projectDir);
457
+ printNextSteps(profile, projectDir, ci, specVerify);
315
458
  });
316
459
 
317
460
  program
@@ -336,6 +479,14 @@ program
336
479
  }
337
480
  }
338
481
 
482
+ for (const rel of KIT_OPTIN_PATHS) {
483
+ const src = join(templateDir, rel);
484
+ const dest = join(projectDir, rel);
485
+ if (!existsSync(src) || !existsSync(dest)) continue;
486
+ copyFileSync(src, dest);
487
+ log.ok(`${rel} (opt-in)`);
488
+ }
489
+
339
490
  log.ok(`Updated to v${KIT_VERSION}`);
340
491
  log.info('Run ./scripts/sync-local-agent-skills.sh to sync to local IDE');
341
492
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-orchestrator-kit",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Universal AI agent orchestration kit for Cursor, Claude Code, and Amp Code — spec-driven pipeline with OpenSpec integration",
5
5
  "keywords": [
6
6
  "ai-agent",
@@ -0,0 +1,36 @@
1
+ .agent-verify-base:
2
+ image: node:20
3
+ before_script:
4
+ - |
5
+ if [ -f pnpm-lock.yaml ]; then
6
+ export PM=pnpm
7
+ corepack enable
8
+ pnpm install --frozen-lockfile
9
+ elif [ -f yarn.lock ]; then
10
+ export PM=yarn
11
+ yarn install --frozen-lockfile
12
+ else
13
+ export PM=npm
14
+ npm ci
15
+ fi
16
+ script:
17
+ - npx openspec validate --all --strict
18
+ - |
19
+ run_pm_script() {
20
+ local name=$1
21
+ if [ "$PM" = "npm" ]; then
22
+ npm run "$name" --if-present
23
+ elif node -e "process.exit(require('./package.json').scripts?.['${name}'] ? 0 : 1)"; then
24
+ $PM run "$name"
25
+ fi
26
+ }
27
+ run_pm_script lint
28
+ run_pm_script build
29
+ run_pm_script test
30
+
31
+ agent-verify:
32
+ extends: .agent-verify-base
33
+ rules:
34
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
35
+ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
36
+ - if: $CI_COMMIT_BRANCH == "develop"
@@ -0,0 +1,47 @@
1
+ # ──────────────────────────────────────────────────────────────
2
+ # AI Spec Verifier — blocking gate on merge requests.
3
+ # Verifies that changed src/ code complies with openspec/specs/
4
+ # via Amp CLI, posts a PASS / BLOCKED comment to the MR, and
5
+ # fails the pipeline on a BLOCKED verdict.
6
+ #
7
+ # Installed by agent-orchestrator-kit (init --ci gitlab --spec-verify).
8
+ # Include from .gitlab-ci.yml:
9
+ # include:
10
+ # - local: '.gitlab/spec-verify.yml'
11
+ #
12
+ # Required CI/CD variables (Settings → CI/CD → Variables):
13
+ # AMP_API_KEY — Amp API key (masked, protected)
14
+ # GITLAB_VERIFIER_TOKEN — Project token with api scope (masked)
15
+ # ──────────────────────────────────────────────────────────────
16
+ .spec-verify-base:
17
+ image: node:20
18
+ script:
19
+ - apt-get update -qq && apt-get install -y -qq python3 curl git > /dev/null
20
+ - npm install -g @sourcegraph/amp@latest
21
+ - chmod +x scripts/verify-specs.sh scripts/post-mr-verdict.sh
22
+ - bash scripts/verify-specs.sh
23
+ - bash scripts/post-mr-verdict.sh
24
+ # Evaluate verdict — exit 1 if verifier says fail
25
+ - |
26
+ PASS=$(python3 -c "import json; v=json.load(open('artifacts/verdict.json')); print(str(v.get('pass',True)).lower())")
27
+ if [ "$PASS" != "true" ]; then
28
+ echo "Spec verifier found errors. See MR comment for details."
29
+ exit 1
30
+ fi
31
+ echo "Spec verification passed."
32
+ artifacts:
33
+ paths:
34
+ - artifacts/verdict.json
35
+ - artifacts/verifier-prompt.md
36
+ expire_in: 7 days
37
+ when: always
38
+
39
+ spec-verify:
40
+ extends: .spec-verify-base
41
+ rules:
42
+ - if: $CI_PIPELINE_SOURCE == "merge_request_event"
43
+ changes:
44
+ - "src/**/*"
45
+ # Phase 1 (warning-only rollout): uncomment the next line to let
46
+ # the pipeline stay green while the team builds trust in verdicts.
47
+ # allow_failure: true
@@ -0,0 +1,7 @@
1
+ include:
2
+ - local: '.gitlab/agent-verify.yml'
3
+ # AI Spec Verifier (opt-in — installed via init --ci gitlab --spec-verify):
4
+ # - local: '.gitlab/spec-verify.yml'
5
+
6
+ agent-verify:
7
+ extends: .agent-verify-base
@@ -24,6 +24,10 @@ Never mix phases in one chat — this is the single most important rule.
24
24
  | Implementer | `/opsx:apply <name>` | writes `src/` | strong |
25
25
  | Verifier | CI (automatic) | scripts only | — |
26
26
 
27
+ Verifier runs on **GitHub Actions** (default) or **GitLab** via `prebuild` → `verify:openspec` when using `init --ci gitlab`. GitLab projects do not use `.github/workflows/`.
28
+
29
+ With `init --ci gitlab --spec-verify`, an **AI Spec Verifier** also runs on MRs changing `src/`: an Amp agent checks the changed code against `openspec/specs/` and a **BLOCKED verdict fails the pipeline** (gate `spec-verify-blocking` in `.agents/orchestrator.yaml`).
30
+
27
31
  ## Hard Rules
28
32
 
29
33
  - **One active change per developer** at a time.
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env bash
2
+ # ──────────────────────────────────────────────────────────────
3
+ # Posts spec verifier verdict as a GitLab MR comment.
4
+ #
5
+ # Installed by agent-orchestrator-kit (init --ci gitlab --spec-verify).
6
+ #
7
+ # Usage: ./scripts/post-mr-verdict.sh
8
+ # Env: CI_API_V4_URL, CI_PROJECT_ID, CI_MERGE_REQUEST_IID,
9
+ # GITLAB_VERIFIER_TOKEN (CI/CD variable, masked)
10
+ #
11
+ # Security: GITLAB_VERIFIER_TOKEN is a project access token with
12
+ # api scope. It is NEVER logged or echoed.
13
+ # ──────────────────────────────────────────────────────────────
14
+ set -euo pipefail
15
+
16
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
17
+ VERDICT_FILE="$ROOT/artifacts/verdict.json"
18
+
19
+ if [ ! -f "$VERDICT_FILE" ]; then
20
+ echo "No verdict file found — skipping MR comment."
21
+ exit 0
22
+ fi
23
+
24
+ if [ -z "${CI_API_V4_URL:-}" ] || [ -z "${CI_PROJECT_ID:-}" ] || [ -z "${CI_MERGE_REQUEST_IID:-}" ]; then
25
+ echo "Not running in MR context — skipping MR comment."
26
+ exit 0
27
+ fi
28
+
29
+ if [ -z "${GITLAB_VERIFIER_TOKEN:-}" ]; then
30
+ echo "GITLAB_VERIFIER_TOKEN not set — skipping MR comment."
31
+ exit 0
32
+ fi
33
+
34
+ # Parse verdict
35
+ PASS=$(python3 -c "import json,sys; v=json.load(open('$VERDICT_FILE')); print(str(v.get('pass',True)).lower())")
36
+ SCORE=$(python3 -c "import json,sys; v=json.load(open('$VERDICT_FILE')); print(v.get('score',0))")
37
+ SUMMARY=$(python3 -c "import json,sys; v=json.load(open('$VERDICT_FILE')); print(v.get('summary',''))")
38
+ SKIPPED=$(python3 -c "import json,sys; v=json.load(open('$VERDICT_FILE')); print(str(v.get('skipped',False)).lower())")
39
+
40
+ if [ "$SKIPPED" = "true" ]; then
41
+ ICON="⏭️"
42
+ STATUS="SKIPPED"
43
+ elif [ "$PASS" = "true" ]; then
44
+ ICON="✅"
45
+ STATUS="PASS"
46
+ else
47
+ ICON="❌"
48
+ STATUS="BLOCKED"
49
+ fi
50
+
51
+ # Build findings table
52
+ FINDINGS_TABLE=$(python3 <<'PYEOF'
53
+ import json, sys
54
+
55
+ with open("artifacts/verdict.json") as f:
56
+ v = json.load(f)
57
+
58
+ findings = v.get("findings", [])
59
+ if not findings:
60
+ print("_No findings._")
61
+ sys.exit(0)
62
+
63
+ severity_icons = {"error": "🔴", "warning": "🟡", "info": "🔵"}
64
+
65
+ print("| | Severity | Spec | Requirement | Message | File |")
66
+ print("|---|----------|------|-------------|---------|------|")
67
+ for f in findings:
68
+ icon = severity_icons.get(f.get("severity", "info"), "⚪")
69
+ sev = f.get("severity", "—")
70
+ spec = f.get("spec", "—")
71
+ req = f.get("requirement", "—")
72
+ msg = f.get("message", "—")
73
+ file = f.get("file", "—")
74
+ line = f.get("line")
75
+ if line:
76
+ file = f"{file}:{line}"
77
+ print(f"| {icon} | {sev} | {spec} | {req} | {msg} | {file} |")
78
+ PYEOF
79
+ )
80
+
81
+ # Build comment body
82
+ COMMENT_BODY="## ${ICON} Spec Verifier — ${STATUS}
83
+
84
+ **Score:** ${SCORE}/100
85
+ **Verdict:** ${STATUS}
86
+
87
+ ### Summary
88
+ ${SUMMARY}
89
+
90
+ ### Findings
91
+ ${FINDINGS_TABLE}
92
+
93
+ ---
94
+ _AI Spec Verifier • agent-orchestrator-kit • Pipeline: ${CI_PIPELINE_ID:-local}_"
95
+
96
+ # Escape for JSON
97
+ COMMENT_JSON=$(python3 -c "
98
+ import json, sys
99
+ body = sys.stdin.read()
100
+ print(json.dumps({'body': body}))
101
+ " <<< "$COMMENT_BODY")
102
+
103
+ # Post to GitLab API
104
+ # Security: token is passed via header, never logged
105
+ HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
106
+ --request POST \
107
+ --header "PRIVATE-TOKEN: ${GITLAB_VERIFIER_TOKEN}" \
108
+ --header "Content-Type: application/json" \
109
+ --data "$COMMENT_JSON" \
110
+ "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/merge_requests/${CI_MERGE_REQUEST_IID}/notes")
111
+
112
+ if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
113
+ echo "MR comment posted successfully (HTTP $HTTP_STATUS)"
114
+ else
115
+ echo "Failed to post MR comment (HTTP $HTTP_STATUS)"
116
+ fi
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env bash
2
+ # ──────────────────────────────────────────────────────────────
3
+ # AI Spec Verifier — collects changed src/ files, concatenates
4
+ # openspec/specs/, builds a prompt, calls Amp CLI, and produces
5
+ # artifacts/verdict.json.
6
+ #
7
+ # Installed by agent-orchestrator-kit (init --ci gitlab --spec-verify).
8
+ #
9
+ # Usage: ./scripts/verify-specs.sh
10
+ # Env: CI_MERGE_REQUEST_DIFF_BASE_SHA (GitLab CI provides it)
11
+ # AMP_API_KEY — Amp API key (CI/CD variable, masked)
12
+ # SRC_GLOB — source path filter (default: src/)
13
+ #
14
+ # Security: this script NEVER logs tokens, keys, or .env content.
15
+ # ──────────────────────────────────────────────────────────────
16
+ set -euo pipefail
17
+
18
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
19
+ ARTIFACTS_DIR="$ROOT/artifacts"
20
+ SPECS_DIR="$ROOT/openspec/specs"
21
+ PROJECT_CONFIG="$ROOT/openspec/config.yaml"
22
+ VERDICT_FILE="$ARTIFACTS_DIR/verdict.json"
23
+ PROMPT_FILE="$ARTIFACTS_DIR/verifier-prompt.md"
24
+ SRC_GLOB="${SRC_GLOB:-src/}"
25
+
26
+ mkdir -p "$ARTIFACTS_DIR"
27
+
28
+ write_skipped_verdict() {
29
+ local summary="$1"
30
+ cat > "$VERDICT_FILE" <<EOF
31
+ {
32
+ "pass": true,
33
+ "score": 100,
34
+ "skipped": true,
35
+ "summary": "${summary}",
36
+ "findings": []
37
+ }
38
+ EOF
39
+ }
40
+
41
+ # ── 1. Collect changed source files ──────────────────────────
42
+ BASE_SHA="${CI_MERGE_REQUEST_DIFF_BASE_SHA:-HEAD~1}"
43
+
44
+ CHANGED_FILES=$(git diff --name-only "$BASE_SHA"...HEAD -- "$SRC_GLOB" || true)
45
+
46
+ if [ -z "$CHANGED_FILES" ]; then
47
+ echo "No ${SRC_GLOB} files changed — skipping spec verification."
48
+ write_skipped_verdict "No ${SRC_GLOB} files changed — verification skipped."
49
+ exit 0
50
+ fi
51
+
52
+ echo "Changed files:"
53
+ echo "$CHANGED_FILES"
54
+
55
+ # ── 2. Collect all spec files ────────────────────────────────
56
+ SPEC_FILES=$(find "$SPECS_DIR" -name '*.md' -type f 2>/dev/null || true)
57
+
58
+ if [ -z "$SPEC_FILES" ]; then
59
+ echo "No spec files found in $SPECS_DIR — skipping."
60
+ write_skipped_verdict "No spec files found — verification skipped."
61
+ exit 0
62
+ fi
63
+
64
+ # ── 3. Build spec content block ──────────────────────────────
65
+ SPECS_CONTENT=""
66
+ for spec_file in $SPEC_FILES; do
67
+ rel_path="${spec_file#$ROOT/}"
68
+ SPECS_CONTENT+="
69
+ --- FILE: $rel_path ---
70
+ $(cat "$spec_file")
71
+ "
72
+ done
73
+
74
+ # ── 4. Build changed file content block ──────────────────────
75
+ # Security: skip .env, secrets, tokens, keys from content
76
+ CHANGED_CONTENT=""
77
+ for file in $CHANGED_FILES; do
78
+ full_path="$ROOT/$file"
79
+ if [ -f "$full_path" ]; then
80
+ case "$file" in
81
+ *.env*|*secret*|*token*|*key*|*.pem|*.p12) continue ;;
82
+ esac
83
+ CHANGED_CONTENT+="
84
+ --- FILE: $file ---
85
+ $(cat "$full_path")
86
+ "
87
+ fi
88
+ done
89
+
90
+ # ── 5. Project context from openspec/config.yaml ─────────────
91
+ PROJECT_CONTEXT=""
92
+ if [ -f "$PROJECT_CONFIG" ]; then
93
+ PROJECT_CONTEXT="
94
+ ## Project Context (openspec/config.yaml)
95
+
96
+ $(cat "$PROJECT_CONFIG")
97
+ "
98
+ fi
99
+
100
+ # ── 6. Build verifier prompt ─────────────────────────────────
101
+ cat > "$PROMPT_FILE" <<PROMPT
102
+ You are a SPEC VERIFIER.
103
+ Your job: verify that changed source code complies with project specifications.
104
+ ${PROJECT_CONTEXT}
105
+ ## Instructions
106
+ 1. Read ALL specs below carefully.
107
+ 2. Read ALL changed source files below.
108
+ 3. For each changed file, check if it relates to any spec requirement.
109
+ 4. Verify that every relevant requirement/scenario from specs is satisfied.
110
+ 5. Respect project conventions from the project context above, if provided.
111
+
112
+ ## Output
113
+ Return ONLY valid JSON (no markdown fences, no extra text) with this structure:
114
+ {
115
+ "pass": true|false,
116
+ "score": 0-100,
117
+ "summary": "Brief summary",
118
+ "findings": [
119
+ {
120
+ "severity": "error"|"warning"|"info",
121
+ "spec": "spec file path",
122
+ "requirement": "requirement name from spec",
123
+ "message": "what is wrong or missing",
124
+ "file": "affected source file",
125
+ "line": null
126
+ }
127
+ ]
128
+ }
129
+
130
+ Rules for verdict:
131
+ - "pass": false if ANY finding has severity "error"
132
+ - "pass": true if only "warning" or "info" findings, or no findings
133
+ - "score": 100 minus 10 per error, 3 per warning (minimum 0)
134
+ - If a changed file has no related spec, add an "info" finding noting it
135
+
136
+ ## SPECIFICATIONS
137
+
138
+ ${SPECS_CONTENT}
139
+
140
+ ## CHANGED SOURCE FILES
141
+
142
+ ${CHANGED_CONTENT}
143
+ PROMPT
144
+
145
+ echo "Verifier prompt built ($(wc -c < "$PROMPT_FILE") bytes)"
146
+
147
+ # ── 7. Call Amp CLI ──────────────────────────────────────────
148
+ if ! command -v amp &>/dev/null; then
149
+ echo "amp CLI not found — writing fallback verdict."
150
+ write_skipped_verdict "amp CLI not installed — verification skipped."
151
+ exit 0
152
+ fi
153
+
154
+ if [ -z "${AMP_API_KEY:-}" ]; then
155
+ echo "AMP_API_KEY not set — skipping Amp call."
156
+ write_skipped_verdict "AMP_API_KEY not set in CI — Amp verification skipped."
157
+ exit 0
158
+ fi
159
+
160
+ echo "Running Amp verifier agent..."
161
+ export AMP_API_KEY
162
+ AMP_RESPONSE=$(amp -x < "$PROMPT_FILE" 2>/dev/null || true)
163
+
164
+ # ── 8. Extract JSON from response ────────────────────────────
165
+ # The response might contain markdown fences — strip them
166
+ CLEAN_JSON=$(echo "$AMP_RESPONSE" | sed -n '/^{/,/^}/p' | head -200)
167
+
168
+ if echo "$CLEAN_JSON" | python3 -m json.tool > /dev/null 2>&1; then
169
+ echo "$CLEAN_JSON" > "$VERDICT_FILE"
170
+ else
171
+ echo "Failed to parse verifier response as JSON."
172
+ echo "Raw response (first 500 chars):"
173
+ echo "$AMP_RESPONSE" | head -c 500
174
+ # Write a cautious pass — don't block on verifier infrastructure failure
175
+ cat > "$VERDICT_FILE" <<'EOF'
176
+ {
177
+ "pass": true,
178
+ "score": 50,
179
+ "skipped": false,
180
+ "summary": "Verifier did not return valid JSON — result inconclusive.",
181
+ "findings": [
182
+ {
183
+ "severity": "warning",
184
+ "spec": "N/A",
185
+ "requirement": "N/A",
186
+ "message": "Verifier agent response was not valid JSON. Manual review recommended.",
187
+ "file": "N/A",
188
+ "line": null
189
+ }
190
+ ]
191
+ }
192
+ EOF
193
+ fi
194
+
195
+ echo "Verdict written to $VERDICT_FILE"
196
+ cat "$VERDICT_FILE" | python3 -m json.tool 2>/dev/null || cat "$VERDICT_FILE"