diff --git a/.github/workflows/weekly-update.yml b/.github/workflows/weekly-update.yml
index eb4a65e21..470bde3a1 100644
--- a/.github/workflows/weekly-update.yml
+++ b/.github/workflows/weekly-update.yml
@@ -86,19 +86,28 @@ jobs:
GH_TOKEN: ${{ github.token }}
run: |
BRANCH_NAME="weekly-update-$(date +%Y%m%d)"
- git config user.name "github-actions[bot]"
- git config user.email "github-actions[bot]@users.noreply.github.com"
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${{ github.repository }}.git"
git checkout -b "$BRANCH_NAME"
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
- - name: Run updating skill with Claude Code
- id: claude
- timeout-minutes: 30
+ - uses: SocketDev/socket-registry/.github/actions/setup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
+ with:
+ gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
+
+ - name: Update dependencies (haiku — fast, cheap)
+ id: update
+ timeout-minutes: 10
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GITHUB_ACTIONS: 'true'
run: |
+ if [ -n "$SFW_BIN" ]; then
+ mkdir -p /tmp/sfw-bin
+ printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
+ chmod +x /tmp/sfw-bin/pnpm
+ export PATH="/tmp/sfw-bin:$PATH"
+ fi
+
if [ -z "$ANTHROPIC_API_KEY" ]; then
echo "ANTHROPIC_API_KEY not set - skipping automated update"
echo "success=false" >> $GITHUB_OUTPUT
@@ -106,10 +115,117 @@ jobs:
fi
set +e
- pnpm exec claude --print --dangerously-skip-permissions \
+ pnpm exec claude --print \
+ --allowedTools "Bash(pnpm:*)" "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" "Read" "Write" "Edit" "Glob" "Grep" \
+ --model haiku \
+ --max-turns 15 \
+ "$(cat <<'PROMPT'
+ /updating
+
+
+ You are an automated CI agent in a weekly dependency update workflow.
+ Git is configured with GPG signing. A branch has been created for you.
+
+
+
+ Update all dependencies to their latest versions.
+ Create one atomic commit per dependency update with a conventional commit message.
+ Leave all changes local — the workflow handles pushing and PR creation.
+ Do not run builds or tests — the next step handles that.
+
+
+
+ Each updated dependency has its own commit.
+ The lockfile is consistent with package.json changes.
+ No uncommitted changes remain in the working tree.
+
+ PROMPT
+ )" \
+ 2>&1 | tee claude-update.log
+ CLAUDE_EXIT=${PIPESTATUS[0]}
+ set -e
+
+ if [ "$CLAUDE_EXIT" -eq 0 ]; then
+ echo "success=true" >> $GITHUB_OUTPUT
+ else
+ echo "success=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Run tests
+ id: tests
+ if: steps.update.outputs.success == 'true'
+ run: |
+ if [ -n "$SFW_BIN" ]; then
+ mkdir -p /tmp/sfw-bin
+ printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
+ chmod +x /tmp/sfw-bin/pnpm
+ export PATH="/tmp/sfw-bin:$PATH"
+ fi
+
+ set +e
+ pnpm build 2>&1 | tee build.log
+ BUILD_EXIT=${PIPESTATUS[0]}
+
+ pnpm test 2>&1 | tee test.log
+ TEST_EXIT=${PIPESTATUS[0]}
+ set -e
+
+ if [ "$BUILD_EXIT" -eq 0 ] && [ "$TEST_EXIT" -eq 0 ]; then
+ echo "tests-passed=true" >> $GITHUB_OUTPUT
+ else
+ echo "tests-passed=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Fix test failures (sonnet — smarter, escalated)
+ id: claude
+ if: steps.update.outputs.success == 'true' && steps.tests.outputs.tests-passed == 'false'
+ timeout-minutes: 15
+ env:
+ ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
+ GITHUB_ACTIONS: 'true'
+ run: |
+ if [ -n "$SFW_BIN" ]; then
+ mkdir -p /tmp/sfw-bin
+ printf '#!/bin/bash\nexec "%s" pnpm "$@"\n' "$SFW_BIN" > /tmp/sfw-bin/pnpm
+ chmod +x /tmp/sfw-bin/pnpm
+ export PATH="/tmp/sfw-bin:$PATH"
+ fi
+
+ FAILURE_LOG="$(cat build.log test.log 2>/dev/null)"
+
+ set +e
+ pnpm exec claude --print \
+ --allowedTools "Bash(pnpm:*)" "Bash(git add:*)" "Bash(git commit:*)" "Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" "Bash(git rev-parse:*)" "Read" "Write" "Edit" "Glob" "Grep" \
--model sonnet \
- "/updating - Run the updating skill to update all dependencies. Create atomic commits for each update. You are running in CI mode - skip builds and tests. Do not push or create a PR." \
- 2>&1 | tee claude-output.log
+ --max-turns 25 \
+ "$(cat <
+ You are an automated CI agent in a weekly dependency update workflow.
+ Git is configured with GPG signing. A branch has been created for you.
+ Dependencies were updated in the previous step but build/tests failed.
+
+
+
+ $FAILURE_LOG
+
+
+
+ The dependency updates above caused build or test failures.
+ Diagnose the failures from the logs and fix the code so it builds and tests pass.
+ Create one atomic commit per fix with a conventional commit message.
+ Run pnpm build && pnpm test to verify your fixes.
+ Leave all changes local — the workflow handles pushing and PR creation.
+
+
+
+ pnpm build succeeds.
+ pnpm test succeeds.
+ Each fix has its own commit.
+ No uncommitted changes remain in the working tree.
+
+ PROMPT
+ )" \
+ 2>&1 | tee claude-fix.log
CLAUDE_EXIT=${PIPESTATUS[0]}
set -e
@@ -119,6 +235,38 @@ jobs:
echo "success=false" >> $GITHUB_OUTPUT
fi
+ - name: Set final status
+ id: final
+ if: always()
+ run: |
+ if [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.tests.outputs.tests-passed }}" = "true" ]; then
+ echo "success=true" >> $GITHUB_OUTPUT
+ elif [ "${{ steps.update.outputs.success }}" = "true" ] && [ "${{ steps.claude.outputs.success }}" = "true" ]; then
+ echo "success=true" >> $GITHUB_OUTPUT
+ else
+ echo "success=false" >> $GITHUB_OUTPUT
+ fi
+
+ - name: Validate changes
+ id: validate
+ if: steps.final.outputs.success == 'true'
+ run: |
+ UNEXPECTED=""
+ for file in $(git diff --name-only origin/main..HEAD); do
+ case "$file" in
+ package.json|*/package.json|pnpm-lock.yaml|*/pnpm-lock.yaml|.npmrc|pnpm-workspace.yaml) ;;
+ src/*|test/*) ;;
+ *.ts|*.mts|*.js|*.mjs) ;;
+ *) UNEXPECTED="$UNEXPECTED $file" ;;
+ esac
+ done
+ if [ -n "$UNEXPECTED" ]; then
+ echo "::error::Unexpected files modified by Claude:$UNEXPECTED"
+ echo "valid=false" >> $GITHUB_OUTPUT
+ else
+ echo "valid=true" >> $GITHUB_OUTPUT
+ fi
+
- name: Check for changes
id: changes
run: |
@@ -129,13 +277,13 @@ jobs:
fi
- name: Push branch
- if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
+ if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: git push origin "$BRANCH_NAME"
- name: Create Pull Request
- if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
+ if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
GH_TOKEN: ${{ github.token }}
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
@@ -164,7 +312,7 @@ jobs:
--base main
- name: Add job summary
- if: steps.claude.outputs.success == 'true' && steps.changes.outputs.has-changes == 'true'
+ if: steps.final.outputs.success == 'true' && steps.validate.outputs.valid == 'true' && steps.changes.outputs.has-changes == 'true'
env:
BRANCH_NAME: ${{ steps.branch.outputs.branch }}
run: |
@@ -179,9 +327,16 @@ jobs:
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
with:
name: claude-output-${{ github.run_id }}
- path: claude-output.log
+ path: |
+ claude-update.log
+ claude-fix.log
+ build.log
+ test.log
retention-days: 7
+ - uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@6096b06b1790f411714c89c40f72aade2eeaab7c # main
+ if: always()
+
notify:
name: Notify results
needs: [check-updates, apply-updates]
diff --git a/.npmrc b/.npmrc
index 8ecf23943..a64ba10f3 100644
--- a/.npmrc
+++ b/.npmrc
@@ -6,9 +6,8 @@ link-workspace-packages=false
loglevel=error
prefer-workspace-packages=false
-# Minimum release age - wait 7 days before installing newly published packages
-# pnpm uses minimum-release-age (minutes), npm v11+ uses min-release-age (days)
-minimum-release-age=10080
+# Minimum release age for npm v11+ (days).
+# pnpm equivalent is in pnpm-workspace.yaml (minimumReleaseAge).
min-release-age=7
# Trust policy - prevent downgrade attacks
diff --git a/CLAUDE.md b/CLAUDE.md
index 87fda7d10..73465cb56 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -100,6 +100,7 @@ All shared standards (git, testing, code style, cross-platform, CI) defined in s
- Backward Compatibility: 🚨 FORBIDDEN to maintain - actively remove when encountered (see canonical CLAUDE.md)
- Work Safeguards: MANDATORY commit + backup branch before bulk changes
- Safe Deletion: Use `safeDelete()` from `@socketsecurity/lib/fs` (NEVER `fs.rm/rmSync` or `rm -rf`)
+- HTTP Requests: NEVER use `fetch()` — use `httpJson`/`httpText`/`httpRequest` from `@socketsecurity/lib/http-request`
### Documentation Policy
diff --git a/package.json b/package.json
index ee42f9d69..774dd11ab 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,8 @@
"fix:all": "node scripts/fix.mjs --all",
"lint": "node scripts/lint.mjs",
"lint:all": "node scripts/lint.mjs --all",
+ "format": "oxfmt --write .",
+ "format:check": "oxfmt .",
"// Claude": "",
"claude": "pnpm --filter @socketsecurity/cli run claude --",
"// Type Checking": "",
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index c25ab33c7..0c4158cb8 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -1,3 +1,7 @@
+settings:
+ # Wait 7 days (10080 minutes) before installing newly published packages.
+ minimumReleaseAge: 10080
+
packages:
- packages/*
- '!packages/package-builder/build'