Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
e95db37
chore: remove dead deps, stale docs, and a no-op nightly build step
teallarson Jul 31, 2026
4b80a86
fix: restore static rendering and add toolkit pages to the sitemap
teallarson Jul 31, 2026
dce839e
chore: drop removed build step from workflow flow comment
teallarson Jul 31, 2026
072b21b
refactor: make the toolkit data contract single and enforced
teallarson Jul 31, 2026
71d6ef1
chore: turn on the checks that were inert
teallarson Jul 31, 2026
2925964
fix: enforce --require-complete before skip-unchanged early exit
teallarson Jul 31, 2026
0654c85
test: cover email HAST traversal
teallarson Aug 3, 2026
849e856
fix: restore static rendering and add toolkit pages to the sitemap
teallarson Jul 31, 2026
35ae4f2
refactor: make the toolkit data contract single and enforced
teallarson Jul 31, 2026
c6d77d3
Merge base branch into chore/hardening
teallarson Aug 3, 2026
b584d5e
fix: harden toolkit data loading and parity checks
teallarson Aug 3, 2026
b49d20d
Merge main into chore/single-source-of-truth
teallarson Aug 4, 2026
eb5671a
Merge chore/single-source-of-truth into chore/hardening
teallarson Aug 4, 2026
dacb402
fix: remove fallback integration category
teallarson Aug 4, 2026
543897d
Merge remote-tracking branch 'origin/chore/single-source-of-truth' in…
teallarson Aug 4, 2026
9bb92bc
ci: alert Slack on toolkit generation failure
teallarson Aug 4, 2026
9c811a2
Merge remote-tracking branch 'origin/chore/single-source-of-truth' in…
teallarson Aug 4, 2026
4ead728
Merge main into chore/hardening
teallarson Aug 5, 2026
1d61d5d
refactor: align the generator's module resolution with the app
teallarson Aug 5, 2026
a4b0e61
build: pin tsx instead of resolving it at run time
teallarson Aug 5, 2026
8e828be
Merge remote-tracking branch 'origin/main' into chore/hardening-merged
teallarson Aug 6, 2026
cc56b1c
fix: anchor merge-custom-sections defaults on the script, not the cwd
teallarson Aug 6, 2026
60d274b
fix: preserve healthy docs when toolkit generation degrades
teallarson Aug 6, 2026
6d8fbb6
fix: apply resilient recovery to provider generation
teallarson Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 31 additions & 3 deletions .github/workflows/generate-toolkit-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ jobs:
run: pnpm install --frozen-lockfile

- name: Generate toolkit docs
# Invoked by path rather than through `pnpm exec`, which would reset the
# working directory to the repo root and break the relative paths below.
run: |
pnpm dlx tsx src/cli/index.ts generate \
../node_modules/.bin/tsx src/cli/index.ts generate \
--all \
--skip-unchanged \
--require-complete \
--preserve-last-known-good \
--verbose \
--api-source tool-metadata \
--tool-metadata-url "$ENGINE_API_URL" \
Expand Down Expand Up @@ -81,7 +83,7 @@ jobs:
ANTHROPIC_EDITOR_MODEL: ${{ secrets.ANTHROPIC_EDITOR_MODEL || 'claude-sonnet-4-6' }}

- name: Sync toolkit sidebar navigation
run: pnpm dlx tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections=false --verbose
run: pnpm exec tsx toolkit-docs-generator/scripts/sync-toolkit-sidebar.ts --remove-empty-sections=false --verbose

- name: Create pull request
id: cpr
Expand Down Expand Up @@ -112,6 +114,32 @@ jobs:
env:
GH_TOKEN: ${{ secrets.DOCS_PUBLISHABLE_GH_TOKEN }}

- name: Warn #proj-docs about preserved or omitted toolkit docs
continue-on-error: true
run: |
report=toolkit-docs-generator-verification/logs/failed-tools.json
if [ ! -f "$report" ]; then
exit 0
fi

preserved=$(jq -r '(.preservedToolkits // []) | join(", ")' "$report")
omitted=$(jq -r '(.omittedToolkits // []) | join(", ")' "$report")
if [ -z "$preserved" ] && [ -z "$omitted" ]; then
exit 0
fi

payload=$(jq -n \
--arg run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
--arg preserved "$preserved" \
--arg omitted "$omitted" \
'{text: (":warning: Toolkit docs generation completed with recoverable failures\n\n*Workflow run:* <" + $run_url + "|Open run>" + (if $preserved != "" then "\n*Continuing to serve previous docs:* " + $preserved else "" end) + (if $omitted != "" then "\n*No docs are being served (no prior output):* " + $omitted else "" end))}')

curl --fail-with-body --silent --show-error \
-X POST \
-H "Content-Type: application/json" \
--data "$payload" \
"${{ secrets.SLACK_PROJ_DOCS_WEBHOOK_URL }}"
Comment thread
cursor[bot] marked this conversation as resolved.

alert:
name: Alert on generation failure
if: ${{ always() && needs.generate.result == 'failure' }}
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ jobs:
- name: Run linter
run: pnpm run lint

- name: Run typecheck
run: pnpm run typecheck

- name: Try a build
run: pnpm build

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/translate-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
if: inputs.cleanup_deleted
run: |
echo "🔍 Checking for deleted English files..."
pnpm dlx tsx scripts/i18n-sync/index.ts --cleanup
pnpm exec tsx scripts/i18n-sync/index.ts --cleanup
env:
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}

Expand All @@ -81,7 +81,7 @@ jobs:
echo "🚀 Starting translation process..."

# Build the command with dynamic inputs
CMD="pnpm dlx tsx scripts/i18n-sync/index.ts"
CMD="pnpm exec tsx scripts/i18n-sync/index.ts"

# Add target locale if not 'all'
if [ "${{ inputs.target_locale }}" != "all" ]; then
Expand Down
97 changes: 0 additions & 97 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@
# Exit on any error
set -e

# Detect merge/rebase state — used later to skip the stash+format block
# which conflicts with merge state and corrupts it.
GIT_DIR="$(git rev-parse --git-dir)"
IS_MERGING=false
if [ -f "$GIT_DIR/MERGE_HEAD" ] || \
[ -d "$GIT_DIR/rebase-merge" ] || \
[ -d "$GIT_DIR/rebase-apply" ]; then
IS_MERGING=true
fi

# Check if there are any staged files
if [ -z "$(git diff --cached --name-only)" ]; then
echo "No staged files to check"
Expand Down Expand Up @@ -133,90 +123,3 @@ fi

# --- Lint Staged (formatting) ---
pnpm exec lint-staged

# --- Stash + Format ---
# Skip this block during merge/rebase: git stash --keep-index destroys
# MERGE_HEAD and corrupts the merge state, causing repeated failures.
# lint-staged (above) already handles formatting for staged files safely.
if [ "$IS_MERGING" = true ]; then
echo "⏭️ Skipping stash+format (merge/rebase in progress)"
exit 0
fi

# Store the hash of staged changes to detect modifications
STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)

# Save list of staged files (handling all file states)
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
PARTIALLY_STAGED=$(git diff --name-only)

# If a file is both staged and unstaged, stash/pop can produce conflicts.
# In that case rely on lint-staged only, which already ran above.
if [ -n "$PARTIALLY_STAGED" ] && [ -n "$STAGED_FILES" ]; then
for file in $PARTIALLY_STAGED; do
if [ -f "$file" ] && echo "$STAGED_FILES" | grep -qxF "$file"; then
echo "⏭️ Skipping stash+format (partially staged files detected)"
exit 0
fi
done
fi

# Stash unstaged changes to preserve working directory
# --keep-index keeps staged changes in working tree
STASH_CREATED=false
STASH_MESSAGE="pre-commit-stash-$$-$(date +%s)"
if ! git diff --quiet; then
git stash push --quiet --keep-index --message "$STASH_MESSAGE"
TOP_STASH_SUBJECT="$(git stash list -1 --format='%s' || true)"
case "$TOP_STASH_SUBJECT" in
*"$STASH_MESSAGE")
STASH_CREATED=true
;;
esac
fi

# Run formatter on the staged files
if [ -n "$STAGED_FILES" ]; then
for file in $STAGED_FILES; do
if [ -f "$file" ]; then
pnpm exec ultracite fix "$file"
fi
done
fi
FORMAT_EXIT_CODE=0

# Restore working directory state
if [ "$STASH_CREATED" = true ]; then
# Re-stage the formatted files
if [ -n "$STAGED_FILES" ]; then
echo "$STAGED_FILES" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi

# Restore unstaged changes
if ! git stash pop --quiet; then
echo "❌ Failed to restore stashed changes during pre-commit."
echo " Resolve conflicts, then re-stage files and commit again."
exit 1
fi
else
# No stash was created, just re-add the formatted files
if [ -n "$STAGED_FILES" ]; then
echo "$STAGED_FILES" | while IFS= read -r file; do
if [ -f "$file" ]; then
git add "$file"
fi
done
fi
fi

# Check if staged files actually changed
NEW_STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
if [ "$STAGED_HASH" != "$NEW_STAGED_HASH" ]; then
echo "✨ Files formatted by Ultracite"
fi

exit $FORMAT_EXIT_CODE
22 changes: 12 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@
"start": "next start",
"lint": "pnpm exec ultracite check",
"format": "pnpm exec ultracite fix",
"typecheck": "tsc -p tsconfig.json --noEmit && tsc -p scripts/tsconfig.json --noEmit && tsc -p toolkit-docs-generator/tsconfig.json --noEmit",
"prepare": "husky install",
"translate": "pnpm dlx tsx scripts/i18n-sync/index.ts && pnpm format",
"llmstxt": "pnpm dlx tsx scripts/generate-llmstxt.ts",
"translate": "pnpm exec tsx scripts/i18n-sync/index.ts && pnpm format",
"llmstxt": "pnpm exec tsx scripts/generate-llmstxt.ts",
"test": "vitest --run",
"test:watch": "vitest --watch",
"vale": "vale",
"vale:check": "vale app/en/",
"vale:fix": "pnpm dlx tsx scripts/vale-fix.ts",
"vale:review": "pnpm dlx tsx scripts/vale-style-review.ts",
"vale:editorial": "pnpm dlx tsx scripts/vale-editorial.ts",
"vale:fix": "pnpm exec tsx scripts/vale-fix.ts",
"vale:review": "pnpm exec tsx scripts/vale-style-review.ts",
"vale:editorial": "pnpm exec tsx scripts/vale-editorial.ts",
"vale:sync": "vale sync",
"check-redirects": "pnpm dlx tsx scripts/check-redirects.ts",
"update-links": "pnpm dlx tsx scripts/update-internal-links.ts",
"check-meta": "pnpm dlx tsx scripts/check-meta-keys.ts",
"sync-crawler-config": "pnpm dlx tsx scripts/sync-crawler-config.ts",
"metadata-report": "pnpm dlx tsx toolkit-docs-generator/scripts/report-tool-metadata.ts"
"check-redirects": "pnpm exec tsx scripts/check-redirects.ts",
"update-links": "pnpm exec tsx scripts/update-internal-links.ts",
"check-meta": "pnpm exec tsx scripts/check-meta-keys.ts",
"sync-crawler-config": "pnpm exec tsx scripts/sync-crawler-config.ts",
"metadata-report": "pnpm exec tsx toolkit-docs-generator/scripts/report-tool-metadata.ts"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -91,6 +92,7 @@
"ora": "9.4.0",
"postcss": "8.5.15",
"tailwindcss": "4.3.0",
"tsx": "^4.23.8",
"typescript": "5.9.3",
"ultracite": "6.1.0",
"vite": "7.3.5",
Expand Down
31 changes: 23 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { filterTools } from "../../../app/_components/toolkit-docs/components/available-tools-filter";
import { filterTools } from "@/app/_components/toolkit-docs/components/available-tools-filter";

const makeTool = (
name: string,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { filterTools } from "../../../app/_components/toolkit-docs/components/available-tools-filter";
import { filterTools } from "@/app/_components/toolkit-docs/components/available-tools-filter";

const makeTool = (name: string, operations: string[]) => ({
name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { describe, expect, it } from "vitest";
import { getSharedServiceDomain } from "../../../app/_components/toolkit-docs/components/toolkit-page-utils";
import { getSharedServiceDomain } from "@/app/_components/toolkit-docs/components/toolkit-page-utils";

const makeTool = (domains: string[]) => ({
metadata: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import { tmpdir } from "node:os";
import { basename, join } from "node:path";
import { describe, expect, it } from "vitest";

import {
readToolkitData,
readToolkitIndex,
} from "../../../app/_lib/toolkit-data";
import { readToolkitData, readToolkitIndex } from "@/app/_lib/toolkit-data";

const loadFixture = async (fileName: string): Promise<string> => {
const fixturesDir = new URL("../fixtures/", import.meta.url);
const fixturesDir = new URL(
"../toolkit-docs-generator/tests/fixtures/",
import.meta.url
);
const filePath = new URL(fileName, fixturesDir);
return await readFile(filePath, "utf-8");
};
Expand Down
4 changes: 4 additions & 0 deletions tests/toolkit-markdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,11 @@ const fixture: ToolkitData = {
isPro: false,
type: "arcade",
docsLink: "",
isComingSoon: false,
isHidden: false,
},
auth: null,
documentationChunks: [],
customImports: [],
subPages: [],
tools: [
Expand All @@ -37,6 +40,7 @@ const fixture: ToolkitData = {
required: true,
description: "Who to do the thing for",
enum: null,
inferrable: true,
},
],
auth: { providerId: "demo", providerType: "oauth2", scopes: ["scope.a"] },
Expand Down
Loading
Loading