@getripple/cli 1.0.10 → 1.0.11

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/dist/index.js CHANGED
@@ -3059,12 +3059,30 @@ function intentCommand(action, options) {
3059
3059
  intentStatusCommand(options);
3060
3060
  return;
3061
3061
  }
3062
+ if (action === "push") {
3063
+ void intentPushCommand(options);
3064
+ return;
3065
+ }
3062
3066
  if (action === "close") {
3063
3067
  closeIntentCommand(options);
3064
3068
  return;
3065
3069
  }
3066
3070
  throw new Error("Usage: ripple intent status [--intent latest|path] or ripple intent close --reason <text> [--intent latest|path]");
3067
3071
  }
3072
+ async function intentPushCommand(options) {
3073
+ const workspaceRoot = resolveWorkspaceRoot(".");
3074
+ const intentRef = options.intent ?? "latest";
3075
+ const intent = (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
3076
+ console.log(`[Ripple] Pushing active intent '${intent.id}' to Ripple Cloud...`);
3077
+ const result = await (0, core_1.pushIntentToCloud)(intent);
3078
+ if (result.sent) {
3079
+ console.log("✅ Intent pushed successfully. This is now the active boundary for CI checks.");
3080
+ }
3081
+ else {
3082
+ console.error(`❌ Failed to push intent: ${result.error}`);
3083
+ process.exitCode = 1;
3084
+ }
3085
+ }
3068
3086
  function intentStatusCommand(options) {
3069
3087
  const workspaceRoot = resolveWorkspaceRoot(".");
3070
3088
  const intentRef = options.intent ?? "latest";
@@ -3393,6 +3411,8 @@ async function auditCommand(options) {
3393
3411
  }
3394
3412
  applyStrictExit(options.strict && strictAuditShouldFail(audit));
3395
3413
  }
3414
+ // in: ripple-main/packages/cli/src/index.ts
3415
+ // REPLACE THE ENTIRE OLD `gateCommand` FUNCTION WITH THIS:
3396
3416
  async function gateCommand(options) {
3397
3417
  if (selectedChangeModeCount(options) > 1) {
3398
3418
  throw new Error("Choose one gate/audit mode: --staged, --worktree, or --changed --base <ref>");
@@ -3401,8 +3421,9 @@ async function gateCommand(options) {
3401
3421
  const intentRef = options.intent ?? "latest";
3402
3422
  const mode = selectedChangeMode(options);
3403
3423
  const baseRef = options.base ?? "HEAD";
3424
+ const intentPath = resolveCliIntentPath(workspaceRoot, intentRef);
3404
3425
  try {
3405
- (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
3426
+ (0, core_1.loadChangeIntent)(workspaceRoot, intentPath);
3406
3427
  }
3407
3428
  catch (err) {
3408
3429
  const block = (0, core_1.buildRippleGateIntentBlockSummary)({
@@ -3424,7 +3445,7 @@ async function gateCommand(options) {
3424
3445
  applyStrictExit(true);
3425
3446
  return;
3426
3447
  }
3427
- const audit = await buildAuditFromCliOptions(options);
3448
+ const audit = await buildAuditFromCliOptions({ ...options, intent: intentPath });
3428
3449
  const gate = (0, core_1.buildRippleGateSummary)(audit);
3429
3450
  if (options.json) {
3430
3451
  printJson(gate);
@@ -3435,16 +3456,43 @@ async function gateCommand(options) {
3435
3456
  else {
3436
3457
  printGateSummary(gate);
3437
3458
  }
3438
- // ─── Ripple Cloud Sync ───────────────────────────────────────────────────
3439
- // Records the gate decision to ripple-cloud for the compliance audit trail.
3440
- // Runs on EVERY gate call — including the pre-commit hook blocking scenario.
3441
- // Never blocks. Never throws. The commit result is unaffected by cloud sync.
3459
+ // Consume intent on pass to prevent "ghost intents" if commit is aborted.
3460
+ if (gate.canContinue && intentRef === "latest" && mode === "staged") {
3461
+ try {
3462
+ const consumedIntentCachePath = path.join(workspaceRoot, ".ripple", ".cache", "consumed-intent.json");
3463
+ if (fs.existsSync(consumedIntentCachePath)) {
3464
+ fs.unlinkSync(consumedIntentCachePath);
3465
+ }
3466
+ fs.renameSync(intentPath, consumedIntentCachePath);
3467
+ console.log("\n[Ripple] Intent validated and consumed by gate. Ready for commit.");
3468
+ }
3469
+ catch (err) {
3470
+ const errorDetail = err instanceof Error ? err.message : String(err);
3471
+ console.warn(`[Ripple] CRITICAL WARNING: Could not consume intent file. To prevent a future 'ghost intent' block, manually delete '${intentPath}' after your commit. Error: ${errorDetail}`);
3472
+ }
3473
+ }
3474
+ // Sync a redacted, privacy-safe audit receipt to the cloud.
3442
3475
  if (process.env.RIPPLE_API_KEY) {
3443
3476
  const cloudDecision = gate.canContinue ? "continue" :
3444
3477
  gate.needsHuman ? "human-review" : "blocked";
3445
- const commitSha = (0, core_1.getCurrentCommitSha)(child_process_1.execSync);
3478
+ const commitSha = options.sha ?? (0, core_1.getCurrentCommitSha)(child_process_1.execSync);
3446
3479
  const branch = (0, core_1.getCurrentBranch)(child_process_1.execSync);
3447
3480
  const actor = (0, core_1.getCurrentActor)(child_process_1.execSync);
3481
+ const redactedPayload = {
3482
+ decision: gate.decision,
3483
+ status: gate.status,
3484
+ risk: {
3485
+ level: gate.risk.level,
3486
+ score: gate.risk.score,
3487
+ summary: gate.risk.summary,
3488
+ },
3489
+ auditStatus: audit.status,
3490
+ approvalStatus: audit.approvalStatus.status,
3491
+ intent: audit.intent,
3492
+ mode: audit.mode,
3493
+ baseRef: audit.baseRef,
3494
+ blockingReasons: audit.blockingReasons,
3495
+ };
3448
3496
  const cloudResult = await (0, core_1.syncAuditToCloud)({
3449
3497
  intentId: audit.intent.id,
3450
3498
  decision: cloudDecision,
@@ -3452,42 +3500,16 @@ async function gateCommand(options) {
3452
3500
  branch,
3453
3501
  actor,
3454
3502
  source: "cli",
3455
- payload: {
3456
- // Identity
3457
- intentId: audit.intent.id,
3458
- commitSha,
3459
- branch,
3460
- actor,
3461
- source: "pre-commit-gate",
3462
- // Intent declared scope — what the developer said they would change
3463
- task: audit.intent.task,
3464
- targetFile: audit.intent.targetFile,
3465
- controlMode: audit.intent.controlMode,
3466
- boundaryRisk: audit.intent.boundaryRisk,
3467
- humanGate: audit.intent.humanGate,
3468
- // Gate verdict — what Ripple actually decided
3469
- decision: cloudDecision,
3470
- status: gate.status,
3471
- canContinue: gate.canContinue,
3472
- mustStop: gate.mustStop,
3473
- needsHuman: gate.needsHuman,
3474
- // Evidence — what actually changed
3475
- blockingReasons: audit.blockingReasons ?? [],
3476
- changedFiles: audit.changedFiles ?? [],
3477
- changedOutsideBoundaryFiles: audit.stagedCheck?.intentValidation?.boundaryVerdict?.changedOutsideBoundaryFiles ?? [],
3478
- },
3503
+ payload: redactedPayload,
3479
3504
  });
3480
- if (!options.json && !options.agent) {
3481
- if (cloudResult.sent) {
3482
- const icon = cloudDecision === "continue" ? "✓" : "⛔";
3483
- console.log(`\n[Ripple Cloud] ${icon} Gate decision recorded: ${cloudDecision} (${commitSha.slice(0, 7)})`);
3484
- }
3485
- else if (cloudResult.error) {
3486
- console.warn(`\n[Ripple Cloud] ⚠ Sync failed (non-blocking): ${cloudResult.error}`);
3487
- }
3505
+ if (cloudResult.sent) {
3506
+ const icon = cloudDecision === "continue" ? "✓" : "⛔";
3507
+ console.log(`\n[Ripple Cloud] ${icon} Gate decision recorded: ${cloudDecision} (${commitSha.slice(0, 7)})`);
3508
+ }
3509
+ else if (cloudResult.error) {
3510
+ console.warn(`\n[Ripple Cloud] ⚠ Sync failed (non-blocking): ${cloudResult.error}`);
3488
3511
  }
3489
3512
  }
3490
- // ────────────────────────────────────────────────────────────────────────
3491
3513
  applyStrictExit(options.strict && !gate.canContinue);
3492
3514
  }
3493
3515
  function approveCommand(options) {
@@ -3692,14 +3714,21 @@ function approvalCommand(options) {
3692
3714
  }
3693
3715
  printApprovalStatus(status);
3694
3716
  }
3717
+ // REPLACE WITH THIS NEW FUNCTION
3718
+ // in: ripple-main/packages/cli/src/index.ts
3719
+ // REPLACE THE ENTIRE OLD `ciCommand` FUNCTION WITH THIS:
3695
3720
  async function ciCommand(options) {
3696
3721
  const workspaceRoot = resolveWorkspaceRoot(".");
3697
3722
  const baseRef = options.base ?? defaultCiBaseRef();
3698
- const hasExplicitIntent = Boolean(options.intent);
3699
- const intentRef = options.intent ?? "latest";
3700
3723
  const files = (0, core_1.listGitChangedFiles)(workspaceRoot, baseRef);
3701
3724
  const emitGithubAnnotations = shouldEmitGithubAnnotations(options);
3702
- if (!hasExplicitIntent) {
3725
+ const commitSha = options.sha ?? (0, core_1.getCurrentCommitSha)(child_process_1.execSync);
3726
+ // FETCH THE AUTHORITATIVE INTENT FROM THE CLOUD.
3727
+ const intent = await (0, core_1.fetchActiveIntentForCommit)(commitSha);
3728
+ if (!intent) {
3729
+ // If no active intent is found in the cloud, run a policy-only audit.
3730
+ // This provides a baseline check without enforcing a specific boundary.
3731
+ console.log("[Ripple CI] No active cloud intent found. Running in policy-only audit mode.");
3703
3732
  const summary = await buildCheckSummaryForFiles({
3704
3733
  workspaceRoot,
3705
3734
  files,
@@ -3708,130 +3737,20 @@ async function ciCommand(options) {
3708
3737
  tokenBudget: options.budget,
3709
3738
  });
3710
3739
  const policySync = buildPolicySyncSummary(workspaceRoot);
3711
- if (options.json) {
3712
- printJson({
3713
- ...summary,
3714
- protocol: "ripple-ci-policy-audit",
3715
- version: 1,
3716
- auditMode: true,
3717
- blocking: false,
3718
- intentRequired: false,
3719
- policySync,
3720
- });
3721
- }
3722
- else if (options.agent) {
3723
- printAgentStagedCheckSummary(summary);
3724
- console.log("");
3725
- console.log("ci_mode: policy-audit");
3726
- console.log("blocking: false");
3727
- console.log("intent_required: false");
3728
- console.log(`policy_sync: ${policySync.status}`);
3729
- if (policySync.missingRules.length > 0) {
3730
- console.log("policy_sync_missing_rules:");
3731
- policySync.missingRules.slice(0, 12).forEach((rule) => {
3732
- console.log(`- ${rule.paths.join(", ")} risk=${rule.risk ?? "medium"}`);
3733
- });
3734
- }
3735
- console.log("next_required_action: Review policy-risk findings and policy-sync warnings before merge. Use --intent latest --strict only when you want an intent-bound hard gate.");
3736
- }
3737
- else {
3738
- console.log("Ripple CI policy audit");
3739
- console.log("Status: audit");
3740
- console.log("Blocking: false");
3741
- console.log("Intent: none (local intents are not required in CI audit mode)");
3742
- console.log(`Policy sync: ${policySync.status}`);
3743
- if (policySync.missingRules.length > 0) {
3744
- console.log("");
3745
- console.log("Policy may be missing risky repo surfaces:");
3746
- policySync.missingRules.slice(0, 12).forEach((rule) => {
3747
- console.log(`- ${rule.paths.join(", ")} risk=${rule.risk ?? "medium"}`);
3748
- });
3749
- }
3750
- console.log("");
3751
- printStagedCheckSummary(summary);
3752
- console.log("");
3753
- console.log("Next action: Review policy-risk findings and policy-sync warnings before merge. Use --intent latest --strict only when you want an intent-bound hard gate.");
3754
- }
3755
- if (emitGithubAnnotations && !options.json) {
3740
+ if (emitGithubAnnotations) {
3756
3741
  printGithubPolicyAuditAnnotations(summary, policySync);
3757
- }
3758
- writeGithubPolicyAuditStepSummary(summary, policySync);
3759
- // FOUNDER FIX: Add Cloud Sync to the Policy Audit Path
3760
- if (process.env.RIPPLE_API_KEY) {
3761
- const commitSha = options.sha ?? (0, core_1.getCurrentCommitSha)(child_process_1.execSync);
3762
- const branch = (0, core_1.getCurrentBranch)(child_process_1.execSync);
3763
- const actor = (0, core_1.getCurrentActor)(child_process_1.execSync);
3764
- const syntheticIntentId = `ci-policy-${commitSha.slice(0, 16)}`;
3765
- const cloudResult = await (0, core_1.syncAuditToCloud)({
3766
- protocol: 'ripple-audit', // FOUNDER FIX: Satisfy the strict Zod Cloud Schema
3767
- intentId: syntheticIntentId,
3768
- decision: 'continue',
3769
- commitSha,
3770
- branch,
3771
- actor,
3772
- source: 'ci',
3773
- payload: { mode: 'policy-audit', commitSha, branch, actor },
3774
- });
3775
- if (!options.json && !options.agent) {
3776
- if (cloudResult.sent) {
3777
- console.log(`\n[Ripple Cloud] ✓ Policy audit synced (${commitSha.slice(0, 7)})`);
3778
- }
3779
- else if (cloudResult.error) {
3780
- console.warn(`\n[Ripple Cloud] ⚠ Sync failed: ${cloudResult.error}`);
3781
- }
3782
- }
3783
- }
3784
- return;
3785
- }
3786
- let intent;
3787
- try {
3788
- intent = (0, core_1.loadChangeIntent)(workspaceRoot, intentRef);
3789
- }
3790
- catch (err) {
3791
- const summary = await buildCheckSummaryForFiles({
3792
- workspaceRoot,
3793
- files,
3794
- mode: "changed",
3795
- baseRef,
3796
- tokenBudget: options.budget,
3797
- });
3798
- const message = intentLoadFailureMessage(intentRef, err);
3799
- if (options.json) {
3800
- printJson({
3801
- ...summary,
3802
- nextRequiredPhase: MISSING_INTENT_NEXT_REQUIRED_PHASE,
3803
- nextRequiredAction: MISSING_INTENT_NEXT_REQUIRED_ACTION,
3804
- intentLoadError: {
3805
- intent: intentRef,
3806
- message,
3807
- nextRequiredPhase: MISSING_INTENT_NEXT_REQUIRED_PHASE,
3808
- nextRequiredAction: MISSING_INTENT_NEXT_REQUIRED_ACTION,
3809
- },
3742
+ printGithubNoticeAnnotation({
3743
+ title: "Ripple: Policy Audit Mode",
3744
+ message: "No active intent was found in Ripple Cloud for this commit. To enable strict boundary checks, run `ripple intent push` locally after creating a plan.",
3810
3745
  });
3811
3746
  }
3812
- else if (options.agent) {
3813
- printAgentStagedCheckSummary(summary);
3814
- console.log("");
3815
- console.log(`next_required_phase: ${MISSING_INTENT_NEXT_REQUIRED_PHASE}`);
3816
- console.log(`next_required_action: ${MISSING_INTENT_NEXT_REQUIRED_ACTION}`);
3817
- console.log("");
3818
- console.log("intent_error:");
3819
- console.log(`- ${message}`);
3820
- }
3821
- else {
3822
- printStagedCheckSummary(summary);
3823
- console.log("");
3824
- console.log(`Next required phase: ${MISSING_INTENT_NEXT_REQUIRED_PHASE}`);
3825
- console.log(`Next required action: ${MISSING_INTENT_NEXT_REQUIRED_ACTION}`);
3826
- console.log(`Intent error: ${message}`);
3827
- }
3828
- if (emitGithubAnnotations && !options.json) {
3829
- printGithubIntentLoadError(message);
3830
- }
3831
- writeGithubStepSummary({ summary, intentLoadError: message });
3832
- applyStrictExit(options.strict);
3747
+ writeGithubPolicyAuditStepSummary(summary, policySync);
3748
+ // Policy-only audits do not block the build unless strict mode is on.
3749
+ applyStrictExit(options.strict && summary.requiresAttention);
3833
3750
  return;
3834
3751
  }
3752
+ // An active intent was found. Run the full, strict audit against the cloud boundary.
3753
+ console.log(`[Ripple CI] Active intent '${intent.id}' found in cloud. Running full boundary check.`);
3835
3754
  const audit = await buildAuditForFiles({
3836
3755
  workspaceRoot,
3837
3756
  files,
@@ -3839,13 +3758,10 @@ async function ciCommand(options) {
3839
3758
  baseRef,
3840
3759
  tokenBudget: options.budget,
3841
3760
  intent,
3842
- currentPolicyExplanation: currentPolicyExplanationForIntent(workspaceRoot, intent),
3761
+ currentPolicyExplanation: (0, core_1.explainRipplePolicyForIntent)((0, core_1.loadRipplePolicy)(workspaceRoot), intent),
3843
3762
  });
3844
3763
  if (options.json) {
3845
- printJson({
3846
- ...audit,
3847
- gate: (0, core_1.buildRippleGateSummary)(audit),
3848
- });
3764
+ printJson({ ...audit, gate: (0, core_1.buildRippleGateSummary)(audit) });
3849
3765
  }
3850
3766
  else if (options.agent) {
3851
3767
  printAgentAuditSummary(audit);
@@ -3857,45 +3773,46 @@ async function ciCommand(options) {
3857
3773
  printGithubAuditAnnotations(audit);
3858
3774
  }
3859
3775
  writeGithubAuditStepSummary(audit);
3860
- // ─── Ripple Cloud Sync ──────────────────────────────────────────────────
3861
- // Sends the audit result to ripple-cloud so the GitHub App can verify it.
3862
- // Only runs when RIPPLE_API_KEY is set. Never blocks or throws.
3776
+ // Sync the final audit result back to the cloud for the webhook to read.
3863
3777
  if (process.env.RIPPLE_API_KEY) {
3864
3778
  const gate = (0, core_1.buildRippleGateSummary)(audit);
3865
3779
  const cloudDecision = gate.canContinue ? "continue" :
3866
3780
  gate.needsHuman ? "human-review" : "blocked";
3867
- const commitSha = options.sha ?? (0, core_1.getCurrentCommitSha)(child_process_1.execSync);
3868
3781
  const branch = (0, core_1.getCurrentBranch)(child_process_1.execSync);
3869
3782
  const actor = (0, core_1.getCurrentActor)(child_process_1.execSync);
3783
+ const redactedPayload = {
3784
+ decision: gate.decision,
3785
+ status: gate.status,
3786
+ risk: {
3787
+ level: gate.risk.level,
3788
+ score: gate.risk.score,
3789
+ summary: gate.risk.summary,
3790
+ },
3791
+ auditStatus: audit.status,
3792
+ approvalStatus: audit.approvalStatus.status,
3793
+ intent: audit.intent,
3794
+ mode: audit.mode,
3795
+ baseRef: audit.baseRef,
3796
+ blockingReasons: audit.blockingReasons,
3797
+ };
3870
3798
  const cloudResult = await (0, core_1.syncAuditToCloud)({
3871
3799
  intentId: intent.id,
3872
3800
  decision: cloudDecision,
3873
- commitSha,
3874
- branch,
3875
- actor,
3876
- source: "ci",
3877
- payload: {
3878
- intentId: intent.id,
3879
- commitSha,
3880
- branch,
3881
- actor,
3882
- decision: cloudDecision,
3883
- gate,
3884
- violations: audit.violations?.length ?? 0,
3885
- riskLevel: audit.riskLevel ?? "unknown",
3886
- },
3801
+ commitSha: commitSha,
3802
+ branch: branch,
3803
+ actor: actor,
3804
+ source: 'ci',
3805
+ payload: redactedPayload,
3887
3806
  });
3888
- if (!options.json && !options.agent) {
3889
- if (cloudResult.sent) {
3890
- console.log(`[Ripple Cloud] ✓ Audit synced (${commitSha.slice(0, 7)})`);
3891
- }
3892
- else if (cloudResult.error) {
3893
- console.warn(`[Ripple Cloud] ⚠ Sync failed (non-blocking): ${cloudResult.error}`);
3894
- }
3807
+ if (cloudResult.sent) {
3808
+ console.log(`[Ripple Cloud] ✓ Audit synced (${commitSha.slice(0, 7)})`);
3809
+ }
3810
+ else if (cloudResult.error) {
3811
+ console.warn(`[Ripple Cloud] ⚠ Sync failed (non-blocking): ${cloudResult.error}`);
3895
3812
  }
3896
3813
  }
3897
- // ────────────────────────────────────────────────────────────────────────
3898
- applyStrictExit(options.strict && strictAuditShouldFail(audit));
3814
+ // Exit with a non-zero code if the audit did not pass, failing the CI job.
3815
+ applyStrictExit(!audit.canProceed);
3899
3816
  }
3900
3817
  async function doctorCommand(options) {
3901
3818
  const workspaceRoot = resolveWorkspaceRoot(".");
@@ -4218,77 +4135,20 @@ function ripplePreCommitHookScript() {
4218
4135
  "",
4219
4136
  ].join("\n");
4220
4137
  }
4138
+ // ripple-main/packages/cli/src/index.ts
4139
+ // ... (keep all existing functions)
4221
4140
  function ripplePostCommitHookBlock() {
4222
4141
  return [
4223
4142
  RIPPLE_POST_COMMIT_HOOK_START,
4224
- `# Local intents are consumed after a successful commit.
4225
- # If RIPPLE_API_KEY is set, the commit is synced to ripple-cloud for GitHub App verification.
4143
+ `# After a successful commit, this hook cleans up the consumed intent marker
4144
+ # left by the 'ripple gate' command. This prevents old intents from being
4145
+ # reused accidentally if a commit is amended or rebased.
4226
4146
  set +e
4227
4147
 
4228
- COMMIT_SHA=$(git rev-parse HEAD 2>/dev/null || echo "unknown")
4229
- BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
4230
- ACTOR=$(git config user.name 2>/dev/null || git config user.email 2>/dev/null || echo "unknown")
4231
- CLOUD_URL=\${RIPPLE_CLOUD_URL:-https://your-ripple-cloud.vercel.app}
4232
-
4233
- INTENT_FILE=""
4234
- for intent_file in .ripple/.cache/latest-intent.json .ripple/intents/latest.json; do
4235
- if [ -f "$intent_file" ]; then
4236
- INTENT_FILE="$intent_file"
4237
- break
4238
- fi
4239
- done
4240
-
4241
- # Cloud sync — only if API key is set and we have an intent to sync
4242
- if [ -n "$RIPPLE_API_KEY" ] && [ -n "$INTENT_FILE" ]; then
4243
- INTENT_ID=$(node -e "try{const d=require('fs').readFileSync(process.argv[1],'utf8');console.log(JSON.parse(d).id||'')}catch(e){console.log('')}" "$INTENT_FILE" 2>/dev/null || echo "")
4244
- if [ -n "$INTENT_ID" ]; then
4245
- node -e "
4246
- const https = require('https');
4247
- const http = require('http');
4248
- const url = new URL(process.env.CLOUD_URL + '/api/audit');
4249
- const isHttps = url.protocol === 'https:';
4250
- const body = JSON.stringify({
4251
- intentId: process.env.INTENT_ID,
4252
- decision: 'continue',
4253
- commitSha: process.env.COMMIT_SHA,
4254
- branch: process.env.BRANCH,
4255
- actor: process.env.ACTOR,
4256
- source: 'cli',
4257
- payload: { commitSha: process.env.COMMIT_SHA, source: 'post-commit-hook', branch: process.env.BRANCH }
4258
- });
4259
- const lib = isHttps ? https : http;
4260
- const req = lib.request({
4261
- hostname: url.hostname,
4262
- port: url.port || (isHttps ? 443 : 80),
4263
- path: url.pathname,
4264
- method: 'POST',
4265
- headers: {
4266
- 'Content-Type': 'application/json',
4267
- 'Authorization': 'Bearer ' + process.env.RIPPLE_API_KEY,
4268
- 'Content-Length': Buffer.byteLength(body)
4269
- }
4270
- }, res => {
4271
- if (res.statusCode < 300) process.stdout.write('[Ripple Cloud] Commit synced (' + process.env.COMMIT_SHA.slice(0,7) + ')\\n');
4272
- });
4273
- req.setTimeout(3000, () => req.destroy());
4274
- req.on('error', () => {});
4275
- req.write(body);
4276
- req.end();
4277
- " 2>/dev/null || true
4278
- fi
4279
- fi
4280
-
4281
- # Clear the local intent after sync
4282
- cleared=0
4283
- for intent_file in .ripple/.cache/latest-intent.json .ripple/intents/latest.json; do
4284
- if [ -f "$intent_file" ]; then
4285
- rm "$intent_file"
4286
- cleared=1
4287
- fi
4288
- done
4289
-
4290
- if [ "$cleared" -eq 1 ]; then
4291
- echo "[Ripple] Consumed and cleared local intent."
4148
+ CONSUMED_INTENT_FILE=".ripple/.cache/consumed-intent.json"
4149
+ if [ -f "$CONSUMED_INTENT_FILE" ]; then
4150
+ rm "$CONSUMED_INTENT_FILE"
4151
+ echo "[Ripple] Cleaned up consumed intent marker."
4292
4152
  fi`,
4293
4153
  RIPPLE_POST_COMMIT_HOOK_END,
4294
4154
  "",