projecta-rrr 1.24.3 → 1.24.5
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/AGENTS.md +1 -1
- package/CHANGELOG.md +15 -0
- package/commands/rrr/provision-coordinator.md +7 -2
- package/package.json +1 -1
- package/rrr/lib/team-mode/manager.js +163 -8
- package/scripts/rrr-team-mode.js +10 -2
package/AGENTS.md
CHANGED
|
@@ -96,4 +96,4 @@ The RRR loop follows five steps:
|
|
|
96
96
|
- Use `$rrr-help` for full skill catalogue
|
|
97
97
|
- Trigger phrases are case-sensitive: `$rrr-plan-phase` not `$rrr-PlanPhase`
|
|
98
98
|
|
|
99
|
-
<!-- generated: 2026-05-
|
|
99
|
+
<!-- generated: 2026-05-13T01:06:46.790Z | source: commands/rrr/*.md | count: 59 skills -->
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ All notable changes to RRR will be documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
6
|
|
|
7
|
+
## [1.24.5] - 2026-05-13
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Coordinator auth modes** — `$rrr-provision-coordinator` now writes an `auth` block with `mode: claude-code-subscription` (default) or `mode: anthropic-api-key`. Subscription mode uses `~/.claude/.credentials.json` injected via the Cloudflare Secret `CLAUDE_CREDENTIALS_JSON`, eliminating the need for `ANTHROPIC_API_KEY` on the coordinator.
|
|
11
|
+
- **Cloudflare AI Gateway option** — when `--auth-mode anthropic-api-key`, optional `--ai-gateway-account-id <id> --ai-gateway-id <id> [--ai-gateway-authenticated]` flags wire `ANTHROPIC_BASE_URL` to a gateway URL and add `CF_AI_GATEWAY_TOKEN` to required secrets when authenticated.
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- **Coordinator README** — generated README now includes the sandbox boot snippet that writes `CLAUDE_CREDENTIALS_JSON` to `~/.claude/.credentials.json`, and only documents AI Gateway when API-key mode is selected.
|
|
15
|
+
- **`$rrr-coordinator-status`** — checks for `~/.claude/.credentials.json` or `CLAUDE_CREDENTIALS_JSON` env when subscription mode is active; falls back to API key / AI Gateway env checks otherwise.
|
|
16
|
+
|
|
17
|
+
## [1.24.4] - 2026-05-12
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- **Sprite browser UAT readiness** — live Sprite status now fails when `.planning/RUNTIME.json` requests `gsd-browser` but the Sprite runtime does not have `gsd-browser` on `PATH`.
|
|
21
|
+
|
|
7
22
|
## [1.24.3] - 2026-05-12
|
|
8
23
|
|
|
9
24
|
### Fixed
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: rrr:provision-coordinator
|
|
3
3
|
description: Create the Cloudflare coordinator contract for RRR team mode integration review
|
|
4
|
-
argument-hint: "[--provider cloudflare-sandbox] [--name <coordinator-name>]"
|
|
4
|
+
argument-hint: "[--auth-mode claude-code-subscription|anthropic-api-key] [--ai-gateway-account-id <id> --ai-gateway-id <id> [--ai-gateway-authenticated]] [--provider cloudflare-sandbox] [--name <coordinator-name>]"
|
|
5
5
|
allowed-tools:
|
|
6
6
|
- Read
|
|
7
7
|
- Write
|
|
@@ -32,8 +32,13 @@ Prepare the repo's coordinator contract so a reusable Cloudflare Claude Code/San
|
|
|
32
32
|
|
|
33
33
|
</process>
|
|
34
34
|
|
|
35
|
+
<auth_modes>
|
|
36
|
+
- `claude-code-subscription` (default) — coordinator runs Claude Code authenticated by an Anthropic subscription. Store the contents of `~/.claude/.credentials.json` as the Cloudflare Secret `CLAUDE_CREDENTIALS_JSON`; sandbox boot writes it to disk so Claude Code resumes the session. No `ANTHROPIC_API_KEY` required.
|
|
37
|
+
- `anthropic-api-key` — coordinator uses `ANTHROPIC_API_KEY` (per-token billing). Pair with `--ai-gateway-account-id <id> --ai-gateway-id <id>` to route Anthropic traffic through Cloudflare AI Gateway for caching, rate limits, and observability. Add `--ai-gateway-authenticated` if the gateway requires `cf-aig-authorization: Bearer $CF_AI_GATEWAY_TOKEN`.
|
|
38
|
+
</auth_modes>
|
|
39
|
+
|
|
35
40
|
<constraints>
|
|
36
41
|
- Coordinator is report-first; never auto-merge to the base branch.
|
|
37
42
|
- Developer work remains in team Sprites and team branches.
|
|
38
|
-
- Do not store secrets in planning files.
|
|
43
|
+
- Do not store secrets in planning files — `CLAUDE_CREDENTIALS_JSON` and AI Gateway tokens belong in Cloudflare Secrets.
|
|
39
44
|
</constraints>
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const fs = require('fs');
|
|
4
|
+
const os = require('os');
|
|
4
5
|
const path = require('path');
|
|
5
6
|
const { execFileSync } = require('child_process');
|
|
6
7
|
const { resolvePlanningContext, requireTeamName } = require('./state-router');
|
|
@@ -465,8 +466,14 @@ function readSpriteStatus(projectRoot, options) {
|
|
|
465
466
|
'-lc',
|
|
466
467
|
'printf "branch="; git branch --show-current; printf "\\nhead="; git rev-parse --short HEAD; printf "\\nclaude="; claude --version 2>/dev/null || true; printf "\\ngsd="; gsd-browser --version 2>/dev/null || true'
|
|
467
468
|
]);
|
|
468
|
-
|
|
469
|
+
const needsGsd = (plan.runtime.uat && plan.runtime.uat.browser === 'gsd-browser') ||
|
|
470
|
+
(((plan.runtime.tools || {}).developer_sprite || []).includes('gsd-browser'));
|
|
471
|
+
const hasGsd = /(^|\n)gsd=.+/.test(probe.output || '');
|
|
472
|
+
status.ok = probe.ok && (!needsGsd || hasGsd);
|
|
469
473
|
status.details.push(probe.output || (probe.ok ? 'ok' : 'probe failed'));
|
|
474
|
+
if (probe.ok && needsGsd && !hasGsd) {
|
|
475
|
+
status.details.push('gsd-browser missing from Sprite runtime');
|
|
476
|
+
}
|
|
470
477
|
} else {
|
|
471
478
|
const list = execCapture('sprite', ['list', '--prefix', row.sprite]);
|
|
472
479
|
status.ok = list.ok && list.output.includes(row.sprite);
|
|
@@ -508,6 +515,61 @@ function coordinatorConfigPath(projectRoot) {
|
|
|
508
515
|
return path.join(projectRoot, '.planning', 'coordinator', 'COORDINATOR.json');
|
|
509
516
|
}
|
|
510
517
|
|
|
518
|
+
function buildAiGatewayBlock(opts) {
|
|
519
|
+
const accountId = opts.aiGatewayAccountId || opts.cfAccountId || null;
|
|
520
|
+
const gatewayId = opts.aiGatewayId || null;
|
|
521
|
+
if (!accountId || !gatewayId) return null;
|
|
522
|
+
const authenticated = opts.aiGatewayAuthenticated === true || opts.aiGatewayAuthenticated === 'true';
|
|
523
|
+
const baseUrl = `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/anthropic`;
|
|
524
|
+
return {
|
|
525
|
+
provider: 'cloudflare-ai-gateway',
|
|
526
|
+
account_id: accountId,
|
|
527
|
+
gateway_id: gatewayId,
|
|
528
|
+
anthropic_base_url: baseUrl,
|
|
529
|
+
authenticated,
|
|
530
|
+
runtime_env: { ANTHROPIC_BASE_URL: baseUrl },
|
|
531
|
+
notes: [
|
|
532
|
+
'Only used when auth_mode = anthropic-api-key.',
|
|
533
|
+
'Set ANTHROPIC_BASE_URL to route Anthropic API calls through the gateway.',
|
|
534
|
+
authenticated
|
|
535
|
+
? 'Authenticated gateway: send `cf-aig-authorization: Bearer $CF_AI_GATEWAY_TOKEN` header on every request.'
|
|
536
|
+
: 'Unauthenticated gateway: ANTHROPIC_API_KEY is the only credential; gateway URL is the access boundary.'
|
|
537
|
+
]
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
function buildAuthBlock(opts) {
|
|
542
|
+
const mode = opts.authMode || 'claude-code-subscription';
|
|
543
|
+
if (mode === 'claude-code-subscription') {
|
|
544
|
+
return {
|
|
545
|
+
mode,
|
|
546
|
+
description: 'Coordinator uses Claude Code with an Anthropic subscription (Max/Pro). No ANTHROPIC_API_KEY required.',
|
|
547
|
+
credentials_path: '~/.claude/.credentials.json',
|
|
548
|
+
injection: [
|
|
549
|
+
'Persist the credentials file once (after a one-time `claude login`).',
|
|
550
|
+
'For a Cloudflare sandbox: write the stored credentials JSON to ~/.claude/.credentials.json on container start (from a CF Secret, KV, or D1 row).',
|
|
551
|
+
'For a Sprite: run `claude login` once interactively, then `sprite checkpoint create` to capture the authenticated state.'
|
|
552
|
+
],
|
|
553
|
+
required_secrets: ['GITHUB_TOKEN', 'CLAUDE_CREDENTIALS_JSON'],
|
|
554
|
+
notes: [
|
|
555
|
+
'CLAUDE_CREDENTIALS_JSON holds the contents of ~/.claude/.credentials.json — store as a CF Secret, not in repo.',
|
|
556
|
+
'Subscription billing applies; no per-token API cost.'
|
|
557
|
+
]
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
if (mode === 'anthropic-api-key') {
|
|
561
|
+
return {
|
|
562
|
+
mode,
|
|
563
|
+
description: 'Coordinator authenticates Claude Code with ANTHROPIC_API_KEY (per-token billing).',
|
|
564
|
+
required_secrets: ['ANTHROPIC_API_KEY', 'GITHUB_TOKEN'],
|
|
565
|
+
notes: [
|
|
566
|
+
'Optional: pair with Cloudflare AI Gateway via --ai-gateway-account-id/--ai-gateway-id for caching, rate limits, and observability.'
|
|
567
|
+
]
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
throw new Error(`unknown auth_mode: ${mode}`);
|
|
571
|
+
}
|
|
572
|
+
|
|
511
573
|
function writeCoordinatorProvision(options) {
|
|
512
574
|
const opts = options || {};
|
|
513
575
|
const projectRoot = opts.projectRoot || process.cwd();
|
|
@@ -515,8 +577,13 @@ function writeCoordinatorProvision(options) {
|
|
|
515
577
|
const runtime = readRuntimeManifest(projectRoot);
|
|
516
578
|
const dir = path.join(projectRoot, '.planning', 'coordinator');
|
|
517
579
|
ensureDir(dir);
|
|
580
|
+
const auth = buildAuthBlock(opts);
|
|
581
|
+
const aiGateway = auth.mode === 'anthropic-api-key' ? buildAiGatewayBlock(opts) : null;
|
|
582
|
+
const requiredSecrets = [...auth.required_secrets];
|
|
583
|
+
if (aiGateway && aiGateway.authenticated) requiredSecrets.push('CF_AI_GATEWAY_TOKEN');
|
|
584
|
+
|
|
518
585
|
const config = {
|
|
519
|
-
schema_version:
|
|
586
|
+
schema_version: 2,
|
|
520
587
|
provider: opts.provider || 'cloudflare-sandbox',
|
|
521
588
|
name: opts.name || `rrr-coordinator-${repoNameFor(teamConfig).toLowerCase()}`,
|
|
522
589
|
generated_at: new Date().toISOString(),
|
|
@@ -526,7 +593,9 @@ function writeCoordinatorProvision(options) {
|
|
|
526
593
|
integration_branch: teamConfig.github.integration_branch,
|
|
527
594
|
runtime_manifest: path.relative(projectRoot, runtimeManifestPath(projectRoot)),
|
|
528
595
|
report_first: true,
|
|
529
|
-
|
|
596
|
+
auth,
|
|
597
|
+
ai_gateway: aiGateway,
|
|
598
|
+
required_secrets: requiredSecrets,
|
|
530
599
|
responsibilities: [
|
|
531
600
|
'clone repository into ephemeral sandbox',
|
|
532
601
|
'merge team branches into integration branch',
|
|
@@ -536,6 +605,7 @@ function writeCoordinatorProvision(options) {
|
|
|
536
605
|
]
|
|
537
606
|
};
|
|
538
607
|
writeJson(coordinatorConfigPath(projectRoot), config);
|
|
608
|
+
|
|
539
609
|
const readme = [
|
|
540
610
|
`# ${config.name}`,
|
|
541
611
|
'',
|
|
@@ -548,11 +618,67 @@ function writeCoordinatorProvision(options) {
|
|
|
548
618
|
`Base branch: ${config.base_branch}`,
|
|
549
619
|
`Integration branch: ${config.integration_branch}`,
|
|
550
620
|
`Runtime manifest: ${config.runtime_manifest}`,
|
|
621
|
+
'',
|
|
622
|
+
'## Auth',
|
|
623
|
+
'',
|
|
624
|
+
`Mode: \`${auth.mode}\``,
|
|
625
|
+
'',
|
|
626
|
+
auth.description
|
|
627
|
+
];
|
|
628
|
+
|
|
629
|
+
if (auth.mode === 'claude-code-subscription') {
|
|
630
|
+
readme.push(
|
|
631
|
+
'',
|
|
632
|
+
'### Credentials injection',
|
|
633
|
+
'',
|
|
634
|
+
...auth.injection.map(line => `- ${line}`),
|
|
635
|
+
'',
|
|
636
|
+
'Cloudflare sandbox boot snippet:',
|
|
637
|
+
'',
|
|
638
|
+
'```bash',
|
|
639
|
+
'mkdir -p ~/.claude',
|
|
640
|
+
'printf "%s" "$CLAUDE_CREDENTIALS_JSON" > ~/.claude/.credentials.json',
|
|
641
|
+
'chmod 600 ~/.claude/.credentials.json',
|
|
642
|
+
'claude --version # verifies authenticated',
|
|
643
|
+
'```'
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
readme.push(
|
|
551
648
|
'',
|
|
552
649
|
'## Required Secrets',
|
|
553
650
|
'',
|
|
554
|
-
|
|
555
|
-
|
|
651
|
+
...requiredSecrets.map(s => `- \`${s}\``)
|
|
652
|
+
);
|
|
653
|
+
|
|
654
|
+
if (aiGateway) {
|
|
655
|
+
readme.push(
|
|
656
|
+
'',
|
|
657
|
+
'## Cloudflare AI Gateway (optional)',
|
|
658
|
+
'',
|
|
659
|
+
`Account: ${aiGateway.account_id}`,
|
|
660
|
+
`Gateway: ${aiGateway.gateway_id}`,
|
|
661
|
+
`Anthropic base URL: ${aiGateway.anthropic_base_url}`,
|
|
662
|
+
`Authenticated: ${aiGateway.authenticated ? 'yes' : 'no'}`,
|
|
663
|
+
'',
|
|
664
|
+
'```bash',
|
|
665
|
+
`export ANTHROPIC_BASE_URL="${aiGateway.anthropic_base_url}"`,
|
|
666
|
+
'export ANTHROPIC_API_KEY="$ANTHROPIC_API_KEY"',
|
|
667
|
+
aiGateway.authenticated
|
|
668
|
+
? '# header: cf-aig-authorization: Bearer $CF_AI_GATEWAY_TOKEN'
|
|
669
|
+
: '# unauthenticated gateway — URL is the access boundary',
|
|
670
|
+
'```'
|
|
671
|
+
);
|
|
672
|
+
} else if (auth.mode === 'anthropic-api-key') {
|
|
673
|
+
readme.push(
|
|
674
|
+
'',
|
|
675
|
+
'## Cloudflare AI Gateway',
|
|
676
|
+
'',
|
|
677
|
+
'Not configured. Re-run with `--ai-gateway-account-id <id> --ai-gateway-id <id>` to route Anthropic API traffic through Cloudflare AI Gateway.'
|
|
678
|
+
);
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
readme.push(
|
|
556
682
|
'',
|
|
557
683
|
'## Execution Contract',
|
|
558
684
|
'',
|
|
@@ -560,8 +686,9 @@ function writeCoordinatorProvision(options) {
|
|
|
560
686
|
'- GitHub remains the source of truth for branches, PRs, reviews, and checks.',
|
|
561
687
|
'- Coordinator is report-first: it writes reports and recommendations only; humans approve final merges.',
|
|
562
688
|
'- Developer work stays in Sprites/team branches.'
|
|
563
|
-
|
|
564
|
-
|
|
689
|
+
);
|
|
690
|
+
|
|
691
|
+
atomicWrite(path.join(dir, 'README.md'), `${readme.join('\n')}\n`);
|
|
565
692
|
return { config, runtime, dir };
|
|
566
693
|
}
|
|
567
694
|
|
|
@@ -573,8 +700,36 @@ function readCoordinatorStatus(projectRoot) {
|
|
|
573
700
|
checks.push({ name: 'runtime manifest', ok: fs.existsSync(runtimeManifestPath(root)), detail: path.relative(root, runtimeManifestPath(root)) });
|
|
574
701
|
const wrangler = execCapture('npx', ['wrangler', '--version'], { cwd: root });
|
|
575
702
|
checks.push({ name: 'wrangler available', ok: wrangler.ok, detail: wrangler.output || 'not available' });
|
|
576
|
-
checks.push({ name: 'ANTHROPIC_API_KEY present', ok: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? 'present' : 'missing locally' });
|
|
577
703
|
checks.push({ name: 'GITHUB_TOKEN present', ok: !!process.env.GITHUB_TOKEN, detail: process.env.GITHUB_TOKEN ? 'present' : 'missing locally' });
|
|
704
|
+
|
|
705
|
+
const authMode = config && config.auth ? config.auth.mode : (config && config.ai_gateway ? 'anthropic-api-key' : 'claude-code-subscription');
|
|
706
|
+
if (authMode === 'claude-code-subscription') {
|
|
707
|
+
const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
|
|
708
|
+
const localCreds = fs.existsSync(credsPath);
|
|
709
|
+
checks.push({
|
|
710
|
+
name: 'Claude Code subscription credentials',
|
|
711
|
+
ok: localCreds || !!process.env.CLAUDE_CREDENTIALS_JSON,
|
|
712
|
+
detail: localCreds ? credsPath : (process.env.CLAUDE_CREDENTIALS_JSON ? 'CLAUDE_CREDENTIALS_JSON env present' : 'missing locally (run `claude login` or set CLAUDE_CREDENTIALS_JSON)')
|
|
713
|
+
});
|
|
714
|
+
} else {
|
|
715
|
+
checks.push({ name: 'ANTHROPIC_API_KEY present', ok: !!process.env.ANTHROPIC_API_KEY, detail: process.env.ANTHROPIC_API_KEY ? 'present' : 'missing locally' });
|
|
716
|
+
if (config && config.ai_gateway) {
|
|
717
|
+
const expectedBase = config.ai_gateway.anthropic_base_url;
|
|
718
|
+
const baseUrl = process.env.ANTHROPIC_BASE_URL;
|
|
719
|
+
checks.push({
|
|
720
|
+
name: 'ANTHROPIC_BASE_URL routes to AI Gateway',
|
|
721
|
+
ok: baseUrl === expectedBase,
|
|
722
|
+
detail: baseUrl ? (baseUrl === expectedBase ? expectedBase : `mismatch: ${baseUrl}`) : `missing locally (expected ${expectedBase})`
|
|
723
|
+
});
|
|
724
|
+
if (config.ai_gateway.authenticated) {
|
|
725
|
+
checks.push({
|
|
726
|
+
name: 'CF_AI_GATEWAY_TOKEN present',
|
|
727
|
+
ok: !!process.env.CF_AI_GATEWAY_TOKEN,
|
|
728
|
+
detail: process.env.CF_AI_GATEWAY_TOKEN ? 'present' : 'missing locally (authenticated gateway requires it)'
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
578
733
|
return { config, checks };
|
|
579
734
|
}
|
|
580
735
|
|
package/scripts/rrr-team-mode.js
CHANGED
|
@@ -73,7 +73,7 @@ Usage:
|
|
|
73
73
|
node scripts/rrr-team-mode.js runtime-init
|
|
74
74
|
node scripts/rrr-team-mode.js provision-sprites [--apply] [--teams team-runtime]
|
|
75
75
|
node scripts/rrr-team-mode.js sprite-status [--live]
|
|
76
|
-
node scripts/rrr-team-mode.js provision-coordinator
|
|
76
|
+
node scripts/rrr-team-mode.js provision-coordinator [--auth-mode claude-code-subscription|anthropic-api-key] [--ai-gateway-account-id <id> --ai-gateway-id <id> [--ai-gateway-authenticated]]
|
|
77
77
|
node scripts/rrr-team-mode.js coordinator-status
|
|
78
78
|
|
|
79
79
|
Notes:
|
|
@@ -224,10 +224,18 @@ function main() {
|
|
|
224
224
|
const result = writeCoordinatorProvision({
|
|
225
225
|
projectRoot,
|
|
226
226
|
provider: args.provider,
|
|
227
|
-
name: args.name
|
|
227
|
+
name: args.name,
|
|
228
|
+
authMode: args['auth-mode'] || args.authMode,
|
|
229
|
+
aiGatewayAccountId: args['ai-gateway-account-id'] || args['cf-account-id'],
|
|
230
|
+
aiGatewayId: args['ai-gateway-id'],
|
|
231
|
+
aiGatewayAuthenticated: args['ai-gateway-authenticated'] === true || args['ai-gateway-authenticated'] === 'true'
|
|
228
232
|
});
|
|
229
233
|
console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'COORDINATOR.json'))}`);
|
|
230
234
|
console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'README.md'))}`);
|
|
235
|
+
console.log(`Auth mode: ${result.config.auth.mode}`);
|
|
236
|
+
if (result.config.ai_gateway) {
|
|
237
|
+
console.log(`AI Gateway: ${result.config.ai_gateway.anthropic_base_url}`);
|
|
238
|
+
}
|
|
231
239
|
return;
|
|
232
240
|
}
|
|
233
241
|
|