projecta-rrr 1.24.5 → 1.24.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/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-13T01:06:46.790Z | source: commands/rrr/*.md | count: 59 skills -->
99
+ <!-- generated: 2026-05-13T03:49:42.852Z | source: commands/rrr/*.md | count: 59 skills -->
package/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ 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.6] - 2026-05-13
8
+
9
+ ### Added
10
+ - **Infisical machine-identity auth mode** — `$rrr-provision-coordinator --auth-mode infisical-machine-identity --infisical-project-id <id> --infisical-env <env>` writes a coordinator contract that fetches secrets at boot from Infisical via Universal Auth. Required runtime secrets become `RRR_INFI_CID` + `RRR_INFI_CS` only; Anthropic and GitHub tokens live in Infisical and rotate centrally.
11
+ - **Infisical Sprite bootstrap** — `$rrr-provision-sprites --apply --infisical-project-id <id>` installs Infisical CLI inside each Sprite, writes machine-identity creds, and wires `~/.profile` to fetch `ANTHROPIC_API_KEY` on every login shell. Uses `RRR_INFI_CID` + `RRR_INFI_CS` from the provisioning host's environment.
12
+
13
+ ### Changed
14
+ - **Default coordinator auth mode** — `infisical-machine-identity` is now the default (was `claude-code-subscription`). The subscription path remains available via `--auth-mode claude-code-subscription`.
15
+ - **Coordinator README** — generated README emits the Infisical install + login + secret-fetch boot snippet when in Infisical mode; falls back to credentials-file injection or AI Gateway when in their respective modes.
16
+ - **`$rrr-coordinator-status`** — checks for `RRR_INFI_CID`/`RRR_INFI_CS` env and `infisical --version` when in Infisical mode.
17
+
7
18
  ## [1.24.5] - 2026-05-13
8
19
 
9
20
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projecta-rrr",
3
- "version": "1.24.5",
3
+ "version": "1.24.6",
4
4
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code by Projecta.ai",
5
5
  "bin": {
6
6
  "projecta-rrr": "bin/install.js",
@@ -201,6 +201,9 @@ function buildSpriteProvisionPlan(projectRoot, options) {
201
201
  const workdir = spriteWorkdir(config, runtime);
202
202
  const selectedTeams = normalizeList(opts.teams || opts.team);
203
203
  const teams = selectedTeams.length > 0 ? selectedTeams : Object.keys(config.teams || {});
204
+ const infisicalProjectId = opts.infisicalProjectId || null;
205
+ const infisicalEnv = opts.infisicalEnv || 'dev';
206
+ const infisicalEnabled = !!infisicalProjectId;
204
207
  const rows = [];
205
208
 
206
209
  for (const rawTeam of teams) {
@@ -216,6 +219,34 @@ function buildSpriteProvisionPlan(projectRoot, options) {
216
219
  : `git clone <repo-url> ${workdir}`;
217
220
  const checkoutCommand = `gh auth setup-git >/dev/null 2>&1 || true; git fetch origin && git checkout -B ${teamConfig.branch} origin/${config.github.base_branch}`;
218
221
 
222
+ const loaderScript = [
223
+ '[ -f ~/.infisical-machine-creds ] && source ~/.infisical-machine-creds',
224
+ 'if [ -n "$RRR_INFI_CID" ] && [ -n "$RRR_INFI_CS" ]; then',
225
+ ' _IT=$(infisical login --method=universal-auth --client-id="$RRR_INFI_CID" --client-secret="$RRR_INFI_CS" --plain --silent 2>/dev/null)',
226
+ ' if [ -n "$_IT" ]; then',
227
+ ' export ANTHROPIC_API_KEY=$(infisical secrets get ANTHROPIC_API_KEY --token="$_IT" --projectId="$RRR_INFI_PROJ" --env="${RRR_INFI_ENV:-dev}" --plain --silent 2>/dev/null)',
228
+ ' unset _IT',
229
+ ' fi',
230
+ 'fi'
231
+ ].join('\n');
232
+ const credsScript = [
233
+ 'umask 077 && cat > ~/.infisical-machine-creds <<EOF',
234
+ 'export RRR_INFI_CID="$RRR_INFI_CID"',
235
+ 'export RRR_INFI_CS="$RRR_INFI_CS"',
236
+ 'export RRR_INFI_PROJ="$RRR_INFI_PROJ"',
237
+ 'export RRR_INFI_ENV="$RRR_INFI_ENV"',
238
+ 'EOF'
239
+ ].join('\n');
240
+ const writeLoaderCommand = `cat > ~/.infisical-load.sh <<'LOADER'\n${loaderScript}\nLOADER\ngrep -q "infisical-load.sh" ~/.profile || echo "[ -f ~/.infisical-load.sh ] && source ~/.infisical-load.sh" >> ~/.profile`;
241
+ const infisicalCommands = infisicalEnabled ? [
242
+ '# Install Infisical CLI for runtime secret fetch',
243
+ `sprite exec -s ${teamConfig.sprite} -- bash -lc 'command -v infisical || (curl -1sLf "https://artifacts-cli.infisical.com/setup.deb.sh" | sudo -E bash && sudo apt-get install -y infisical)'`,
244
+ '# Write machine-identity credentials (read-only, mode 600)',
245
+ `sprite exec -s ${teamConfig.sprite} --env "RRR_INFI_CID=$RRR_INFI_CID,RRR_INFI_CS=$RRR_INFI_CS,RRR_INFI_PROJ=${infisicalProjectId},RRR_INFI_ENV=${infisicalEnv}" -- bash -lc ${JSON.stringify(credsScript)}`,
246
+ '# Loader fetches ANTHROPIC_API_KEY on every login shell',
247
+ `sprite exec -s ${teamConfig.sprite} -- bash -lc ${JSON.stringify(writeLoaderCommand)}`
248
+ ] : [];
249
+
219
250
  rows.push({
220
251
  team,
221
252
  github_team: teamConfig.github_team,
@@ -226,6 +257,7 @@ function buildSpriteProvisionPlan(projectRoot, options) {
226
257
  repo,
227
258
  setup,
228
259
  checks,
260
+ infisical: infisicalEnabled ? { project_id: infisicalProjectId, env: infisicalEnv } : null,
229
261
  commands: [
230
262
  `sprite create ${teamConfig.sprite} --skip-console`,
231
263
  `sprite exec -s ${teamConfig.sprite} -- mkdir -p /home/sprite/work`,
@@ -234,12 +266,13 @@ function buildSpriteProvisionPlan(projectRoot, options) {
234
266
  `sprite exec -s ${teamConfig.sprite} --dir ${workdir} --env GITHUB_TOKEN=$GITHUB_TOKEN -- bash -lc '${checkoutCommand}'`,
235
267
  `sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- npx projecta-rrr@latest --global --yes`,
236
268
  ...setup.map(cmd => `sprite exec -s ${teamConfig.sprite} --dir ${workdir} -- bash -lc '${cmd}'`),
269
+ ...infisicalCommands,
237
270
  `sprite checkpoint create -s ${teamConfig.sprite} --comment "rrr clean-ready ${config.milestone} ${team}"`
238
271
  ]
239
272
  });
240
273
  }
241
274
 
242
- return { config, runtime, workdir, rows };
275
+ return { config, runtime, workdir, rows, infisical: infisicalEnabled ? { project_id: infisicalProjectId, env: infisicalEnv } : null };
243
276
  }
244
277
 
245
278
  function renderSpriteProvisionPlan(plan) {
@@ -416,6 +449,31 @@ function applySpriteProvisionPlan(projectRoot, options) {
416
449
  const setup = execCapture('sprite', ['exec', '-s', row.sprite, '--dir', row.workdir, '--', 'bash', '-lc', command]);
417
450
  teamResults.push({ step: `setup: ${command}`, ...setup });
418
451
  }
452
+ if (row.infisical && process.env.RRR_INFI_CID && process.env.RRR_INFI_CS) {
453
+ const cid = process.env.RRR_INFI_CID;
454
+ const cs = process.env.RRR_INFI_CS;
455
+ const proj = row.infisical.project_id;
456
+ const env = row.infisical.env;
457
+ const installInfi = execCapture('sprite', [
458
+ 'exec', '-s', row.sprite, '--', 'bash', '-lc',
459
+ 'command -v infisical || (curl -1sLf "https://artifacts-cli.infisical.com/setup.deb.sh" | sudo -E bash >/dev/null 2>&1 && sudo apt-get install -y infisical >/dev/null 2>&1) && infisical --version'
460
+ ]);
461
+ teamResults.push({ step: 'infisical-install', ...installInfi });
462
+ const writeCreds = execCapture('sprite', [
463
+ 'exec', '-s', row.sprite,
464
+ '--env', `RRR_INFI_CID=${cid},RRR_INFI_CS=${cs},RRR_INFI_PROJ=${proj},RRR_INFI_ENV=${env}`,
465
+ '--', 'bash', '-lc',
466
+ 'umask 077 && cat > ~/.infisical-machine-creds <<EOF\nexport RRR_INFI_CID="$RRR_INFI_CID"\nexport RRR_INFI_CS="$RRR_INFI_CS"\nexport RRR_INFI_PROJ="$RRR_INFI_PROJ"\nexport RRR_INFI_ENV="$RRR_INFI_ENV"\nEOF'
467
+ ]);
468
+ teamResults.push({ step: 'infisical-creds', ...writeCreds });
469
+ const writeLoader = execCapture('sprite', [
470
+ 'exec', '-s', row.sprite, '--', 'bash', '-lc',
471
+ 'cat > ~/.infisical-load.sh <<\'LOADER\'\n[ -f ~/.infisical-machine-creds ] && source ~/.infisical-machine-creds\nif [ -n "$RRR_INFI_CID" ] && [ -n "$RRR_INFI_CS" ]; then\n _IT=$(infisical login --method=universal-auth --client-id="$RRR_INFI_CID" --client-secret="$RRR_INFI_CS" --plain --silent 2>/dev/null)\n if [ -n "$_IT" ]; then\n export ANTHROPIC_API_KEY=$(infisical secrets get ANTHROPIC_API_KEY --token="$_IT" --projectId="$RRR_INFI_PROJ" --env="${RRR_INFI_ENV:-dev}" --plain --silent 2>/dev/null)\n unset _IT\n fi\nfi\nLOADER\ngrep -q "infisical-load.sh" ~/.profile || echo "[ -f ~/.infisical-load.sh ] && source ~/.infisical-load.sh" >> ~/.profile'
472
+ ]);
473
+ teamResults.push({ step: 'infisical-loader', ...writeLoader });
474
+ } else if (row.infisical) {
475
+ teamResults.push({ step: 'infisical-skip', ok: false, output: 'infisical bootstrap requested but RRR_INFI_CID/RRR_INFI_CS not set in apply environment' });
476
+ }
419
477
  const readyForCheckpoint = teamResults.every(result => result.ok);
420
478
  const checkpoint = readyForCheckpoint
421
479
  ? execCapture('sprite', [
@@ -539,7 +597,32 @@ function buildAiGatewayBlock(opts) {
539
597
  }
540
598
 
541
599
  function buildAuthBlock(opts) {
542
- const mode = opts.authMode || 'claude-code-subscription';
600
+ const mode = opts.authMode || 'infisical-machine-identity';
601
+ if (mode === 'infisical-machine-identity') {
602
+ const projectId = opts.infisicalProjectId || null;
603
+ const env = opts.infisicalEnv || 'dev';
604
+ const domain = opts.infisicalDomain || 'https://app.infisical.com';
605
+ const secrets = opts.infisicalSecrets && opts.infisicalSecrets.length > 0
606
+ ? opts.infisicalSecrets
607
+ : ['ANTHROPIC_API_KEY', 'GITHUB_TOKEN'];
608
+ return {
609
+ mode,
610
+ description: 'Coordinator fetches secrets at boot from Infisical via Universal Auth (machine identity).',
611
+ infisical: {
612
+ domain,
613
+ project_id: projectId,
614
+ env,
615
+ secrets
616
+ },
617
+ required_runtime_env: ['RRR_INFI_CID', 'RRR_INFI_CS'],
618
+ required_secrets: ['RRR_INFI_CID', 'RRR_INFI_CS'],
619
+ notes: [
620
+ 'RRR_INFI_CID + RRR_INFI_CS are Infisical Universal Auth client credentials (stored as Cloudflare Secrets / Wrangler secrets).',
621
+ 'Boot script calls `infisical login --method=universal-auth` then `infisical secrets get` for each declared secret.',
622
+ 'Anthropic, GitHub, and any future tokens rotate centrally in Infisical with no coordinator redeploy.'
623
+ ]
624
+ };
625
+ }
543
626
  if (mode === 'claude-code-subscription') {
544
627
  return {
545
628
  mode,
@@ -626,7 +709,38 @@ function writeCoordinatorProvision(options) {
626
709
  auth.description
627
710
  ];
628
711
 
629
- if (auth.mode === 'claude-code-subscription') {
712
+ if (auth.mode === 'infisical-machine-identity') {
713
+ const infi = auth.infisical;
714
+ const fetches = (infi.secrets || []).map(s =>
715
+ `export ${s}=$(infisical secrets get ${s} --token="$_IT" --projectId="${infi.project_id || '<INFISICAL_PROJECT_ID>'}" --env="${infi.env}" --plain --silent)`
716
+ );
717
+ readme.push(
718
+ '',
719
+ '### Infisical machine-identity bootstrap',
720
+ '',
721
+ `- Domain: ${infi.domain}`,
722
+ `- Project ID: ${infi.project_id || '(unset — pass --infisical-project-id)'}`,
723
+ `- Env: ${infi.env}`,
724
+ `- Fetched secrets: ${(infi.secrets || []).join(', ') || '(none)'}`,
725
+ '',
726
+ 'Cloudflare sandbox boot snippet:',
727
+ '',
728
+ '```bash',
729
+ '# Install Infisical CLI (one-time, ideally baked into image)',
730
+ 'curl -1sLf "https://artifacts-cli.infisical.com/setup.deb.sh" | sudo -E bash',
731
+ 'sudo apt-get install -y infisical',
732
+ '',
733
+ '# Exchange machine-identity credentials for an access token',
734
+ '_IT=$(infisical login --method=universal-auth --client-id="$RRR_INFI_CID" --client-secret="$RRR_INFI_CS" --plain --silent)',
735
+ '',
736
+ '# Fetch and export each declared secret',
737
+ ...fetches,
738
+ 'unset _IT',
739
+ '',
740
+ 'claude --version # picks up ANTHROPIC_API_KEY automatically',
741
+ '```'
742
+ );
743
+ } else if (auth.mode === 'claude-code-subscription') {
630
744
  readme.push(
631
745
  '',
632
746
  '### Credentials injection',
@@ -702,8 +816,25 @@ function readCoordinatorStatus(projectRoot) {
702
816
  checks.push({ name: 'wrangler available', ok: wrangler.ok, detail: wrangler.output || 'not available' });
703
817
  checks.push({ name: 'GITHUB_TOKEN present', ok: !!process.env.GITHUB_TOKEN, detail: process.env.GITHUB_TOKEN ? 'present' : 'missing locally' });
704
818
 
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') {
819
+ const authMode = config && config.auth ? config.auth.mode : (config && config.ai_gateway ? 'anthropic-api-key' : 'infisical-machine-identity');
820
+ if (authMode === 'infisical-machine-identity') {
821
+ checks.push({
822
+ name: 'RRR_INFI_CID present',
823
+ ok: !!process.env.RRR_INFI_CID,
824
+ detail: process.env.RRR_INFI_CID ? 'present' : 'missing locally (Infisical Universal Auth client ID)'
825
+ });
826
+ checks.push({
827
+ name: 'RRR_INFI_CS present',
828
+ ok: !!process.env.RRR_INFI_CS,
829
+ detail: process.env.RRR_INFI_CS ? 'present' : 'missing locally (Infisical Universal Auth client secret)'
830
+ });
831
+ const infiCli = execCapture('infisical', ['--version']);
832
+ checks.push({
833
+ name: 'infisical CLI available',
834
+ ok: infiCli.ok,
835
+ detail: infiCli.output || 'not installed locally; coordinator runtime must install it'
836
+ });
837
+ } else if (authMode === 'claude-code-subscription') {
707
838
  const credsPath = path.join(os.homedir(), '.claude', '.credentials.json');
708
839
  const localCreds = fs.existsSync(credsPath);
709
840
  checks.push({
@@ -189,7 +189,9 @@ function main() {
189
189
  if (command === 'provision-sprites') {
190
190
  const options = {
191
191
  teams: args.teams,
192
- team: args.team
192
+ team: args.team,
193
+ infisicalProjectId: args['infisical-project-id'],
194
+ infisicalEnv: args['infisical-env']
193
195
  };
194
196
  if (args.apply === true || args.apply === 'true') {
195
197
  const result = applySpriteProvisionPlan(projectRoot, options);
@@ -226,6 +228,10 @@ function main() {
226
228
  provider: args.provider,
227
229
  name: args.name,
228
230
  authMode: args['auth-mode'] || args.authMode,
231
+ infisicalProjectId: args['infisical-project-id'],
232
+ infisicalEnv: args['infisical-env'],
233
+ infisicalDomain: args['infisical-domain'],
234
+ infisicalSecrets: splitCsv(args['infisical-secrets']),
229
235
  aiGatewayAccountId: args['ai-gateway-account-id'] || args['cf-account-id'],
230
236
  aiGatewayId: args['ai-gateway-id'],
231
237
  aiGatewayAuthenticated: args['ai-gateway-authenticated'] === true || args['ai-gateway-authenticated'] === 'true'
@@ -233,6 +239,9 @@ function main() {
233
239
  console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'COORDINATOR.json'))}`);
234
240
  console.log(`Wrote ${path.relative(projectRoot, path.join(result.dir, 'README.md'))}`);
235
241
  console.log(`Auth mode: ${result.config.auth.mode}`);
242
+ if (result.config.auth && result.config.auth.infisical) {
243
+ console.log(`Infisical: ${result.config.auth.infisical.project_id || '(unset)'}@${result.config.auth.infisical.env}`);
244
+ }
236
245
  if (result.config.ai_gateway) {
237
246
  console.log(`AI Gateway: ${result.config.ai_gateway.anthropic_base_url}`);
238
247
  }