Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
135 changes: 135 additions & 0 deletions .claude/commands/do-release.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
---
name: do-release
description: Cut a new release of all @learnkit-ai packages. Triggers the GitHub Actions release pipeline - bumps versions across all packages, opens a bump PR, then tags and publishes to npm after merge.
---

# Release @learnkit-ai packages

Run this whenever you want to cut a new version. Walks through every step and confirms each one.

## Step 1 - Determine bump type

If the user did not pass an argument, ask:

> What kind of release is this?
>
> - `patch` - bug fixes only (0.1.1 -> 0.1.2)
> - `minor` - new features, backward-compatible (0.1.1 -> 0.2.0)
> - `major` - breaking changes (0.1.1 -> 1.0.0)

Use the argument directly if provided: `/do-release patch`, `/do-release minor`, `/do-release major`.

## Step 2 - Verify you are on main and clean

```bash
git checkout main && git pull
git status
```

If there are uncommitted changes, stop and tell the user to commit or stash them first.

## Step 3 - Confirm current versions

```bash
node -p "require('./packages/core/package.json').version"
```

Show the user: "Current version is X.Y.Z - this will bump to A.B.C across all packages (schemas, core, react, cli). Proceed?"

Wait for confirmation before continuing.

## Step 4 - Trigger the Release workflow

```bash
gh workflow run release.yml --field bump=<patch|minor|major>
```

Then immediately check the run started:

```bash
sleep 3 && gh run list --workflow=release.yml --limit=1
```

## Step 5 - Watch the Release workflow

```bash
gh run watch <run-id>
```

The workflow will:
- Run typecheck and tests across all packages
- Bump version in all 4 package.json files (schemas, core, react, cli)
- Push branch `chore/bump-vX.Y.Z`
- Open a PR automatically

If the workflow fails at "Push branch and open PR" with a `createPullRequest` permission error:
- The branch was still pushed - create the PR manually:
```bash
gh pr create --title "Bump version to X.Y.Z" \
--body "Automated version bump. Merging triggers tag creation and npm publish." \
--base main --head chore/bump-vX.Y.Z
```
- Ask user to enable: Settings -> Actions -> General -> "Allow GitHub Actions to create and approve pull requests"

## Step 6 - Show the bump PR

```bash
gh pr list --head chore/bump-v --limit=1
```

Show the PR URL and tell the user:

> PR is open. Once CI passes, merge it to trigger the tag and npm publish automatically.

## Step 7 - After the user merges the bump PR

Pull main and verify:

```bash
git checkout main && git pull
node -p "require('./packages/core/package.json').version"
```

Watch `tag-on-merge.yml` fire:

```bash
gh run list --workflow=tag-on-merge.yml --limit=1
gh run watch <run-id>
```

## Step 8 - Watch npm publish

Once `tag-on-merge.yml` creates the release, `publish.yml` fires automatically:

```bash
gh run list --workflow=publish.yml --limit=1
gh run watch <run-id>
```

## Step 9 - Confirm release is live

```bash
npm show @learnkit-ai/core version
gh release view vX.Y.Z
```

Tell the user:

> All packages published to npm at vX.Y.Z:
> - `@learnkit-ai/schemas@X.Y.Z`
> - `@learnkit-ai/core@X.Y.Z`
> - `@learnkit-ai/react@X.Y.Z`
> - `@learnkit-ai/cli@X.Y.Z`
>
> Release: https://github.com/learnkit-ai/learnkit/releases/tag/vX.Y.Z

---

## Known gotchas

| Problem | Cause | Fix |
|---------|-------|-----|
| `createPullRequest` error in workflow | Repo setting not enabled | Settings -> Actions -> General -> enable "Allow GitHub Actions to create and approve pull requests" |
| `tag-on-merge` skips tag creation | Merge commit message doesn't contain `chore/bump-v` | Check that the bump branch was named `chore/bump-vX.Y.Z` |
| `publish.yml` fails with 401 | NPM_TOKEN secret expired | Run `gh secret set NPM_TOKEN --body NEW_TOKEN --repo learnkit-ai/learnkit` |
| Orphaned tag pointing to wrong commit | Cancelled push | `git push origin --delete refs/tags/vX.Y.Z` then re-run |
88 changes: 88 additions & 0 deletions .claude/commands/update-website.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
---
name: update-website
description: Sync apps/web with the latest package versions - update install snippets, version numbers in docs, and any copy that references the CLI or packages.
---

# Update learnkit-ai.com

Keeps `apps/web` in sync with the current state of the packages.
Run this after every release to make sure the site reflects what is actually shipped.

## Step 1 - Read the current version

```bash
node -p "require('./packages/core/package.json').version"
```

Note the version. All install snippets and version references on the site should match.

## Step 2 - Check what changed since last release

```bash
git log --oneline $(git describe --tags --abbrev=0 HEAD^)..HEAD
```

Skim the log for:
- New roles or tools added -> update DemoFlow options
- New API surface -> update /developers page and code examples
- Breaking changes -> update install/getting-started copy
- Bug fixes worth calling out -> consider a blog post

## Step 3 - Update install snippets

Search for any hardcoded version numbers or install commands:

```bash
grep -r "@learnkit-ai" apps/web/src --include="*.tsx" --include="*.ts" --include="*.mdx" -l
```

For each file found, update:
- `pnpm add @learnkit-ai/core @learnkit-ai/react` (no version pin needed - latest is fine)
- Any hardcoded version strings like `@0.1.1` -> `@X.Y.Z`

## Step 4 - Update the developers page

File: `apps/web/src/app/developers/page.tsx` (or `components/developers/`)

Check:
- Code block in `CodeBlock.tsx` - does the import example match current API?
- `DeveloperHero.tsx` - version badge shows correct version

## Step 5 - Update roles and tools if new ones shipped

Source of truth:
```bash
node -e "const {getSupportedRoles, getSupportedTools} = require('./packages/core/dist/index.cjs'); console.log(getSupportedRoles()); console.log(getSupportedTools());"
```

Check against `apps/web/src/components/demo/DemoFlow.tsx` - the role/tool dropdowns should list all supported values.

## Step 6 - Verify the build

```bash
pnpm --filter @learnkit-ai/web build 2>&1 | tail -20
```

Fix any TypeScript or missing import errors before committing.

## Step 7 - Commit and push via PR

```bash
git checkout -b chore/sync-website-vX.Y.Z
git add apps/web/src/
git commit -m "Sync website with vX.Y.Z"
git push -u origin chore/sync-website-vX.Y.Z
gh pr create --title "Sync website with vX.Y.Z" --body "Updates install snippets, version references, and any copy that changed in this release."
```

---

## Quick reference - what lives where

| What | Source of truth | Website file |
|------|----------------|--------------|
| Supported roles | `packages/core/src/roles.ts` | `apps/web/src/components/demo/DemoFlow.tsx` |
| Supported tools | `packages/core/src/tools.ts` | `apps/web/src/components/demo/DemoFlow.tsx` |
| Install command | `packages/core/package.json` version | `apps/web/src/components/developers/CodeBlock.tsx` |
| API surface | `packages/core/src/index.ts` | `apps/web/src/app/developers/page.tsx` |
| React API | `packages/react/src/index.ts` | `apps/web/src/components/developers/CodeBlock.tsx` |
60 changes: 60 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Publish to npm

# Step 3 of 3: fires when a GitHub Release is published (triggered by tag-on-merge.yml).
# Publishes @learnkit-ai/schemas, @learnkit-ai/core, @learnkit-ai/react, @learnkit-ai/cli.

on:
release:
types: [published]
workflow_dispatch:
inputs:
ref:
description: 'Tag or branch to publish (e.g. v0.1.2)'
required: true

jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.ref }}

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm
registry-url: https://registry.npmjs.org

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Build all packages
run: |
pnpm --filter @learnkit-ai/schemas build
pnpm --filter @learnkit-ai/core build
pnpm --filter @learnkit-ai/react build
pnpm --filter @learnkit-ai/cli build

- name: Run checks
run: |
pnpm --filter @learnkit-ai/schemas typecheck
pnpm --filter @learnkit-ai/core typecheck
pnpm --filter @learnkit-ai/react typecheck
pnpm --filter @learnkit-ai/schemas test
pnpm --filter @learnkit-ai/core test
pnpm --filter @learnkit-ai/react test

- name: Publish packages (schemas first, then dependents)
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
pnpm --filter @learnkit-ai/schemas publish --no-git-checks --access public
pnpm --filter @learnkit-ai/core publish --no-git-checks --access public
pnpm --filter @learnkit-ai/react publish --no-git-checks --access public
pnpm --filter @learnkit-ai/cli publish --no-git-checks --access public
94 changes: 94 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
name: Release

# Step 1 of 3: bump all package versions on a branch and open a PR.
# Step 2 is tag-on-merge.yml — creates the git tag + GitHub Release when this PR lands.
# Step 3 is publish.yml — publishes all packages to npm when the release is created.

on:
workflow_dispatch:
inputs:
bump:
description: 'Version bump type'
required: true
default: 'patch'
type: choice
options:
- patch
- minor
- major

jobs:
bump:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: 20
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Run CI checks
run: |
pnpm --filter @learnkit-ai/schemas typecheck
pnpm --filter @learnkit-ai/core typecheck
pnpm --filter @learnkit-ai/react typecheck
pnpm --filter @learnkit-ai/cli typecheck
pnpm --filter @learnkit-ai/schemas test
pnpm --filter @learnkit-ai/core test
pnpm --filter @learnkit-ai/react test

- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Bump all package versions
id: bump
run: |
OLD=$(node -p "require('./packages/core/package.json').version")
node -e "
const fs = require('fs');
const semver = require('./node_modules/.pnpm/semver@*/node_modules/semver') || { inc: (v,t) => { const p=v.split('.').map(Number); if('${{ inputs.bump }}'==='major'){p[0]++;p[1]=0;p[2]=0}else if('${{ inputs.bump }}'==='minor'){p[1]++;p[2]=0}else{p[2]++};return p.join('.'); } };
const bump = '${{ inputs.bump }}';
const pkgs = ['packages/schemas/package.json','packages/core/package.json','packages/react/package.json','packages/cli/package.json'];
let newV;
pkgs.forEach(f => {
const p = JSON.parse(fs.readFileSync(f,'utf8'));
const parts = p.version.split('.').map(Number);
if(bump==='major'){parts[0]++;parts[1]=0;parts[2]=0}
else if(bump==='minor'){parts[1]++;parts[2]=0}
else{parts[2]++}
newV = parts.join('.');
p.version = newV;
fs.writeFileSync(f, JSON.stringify(p,null,2)+'\n');
console.log(f, newV);
});
"
NEW=$(node -p "require('./packages/core/package.json').version")
echo "old=$OLD" >> $GITHUB_OUTPUT
echo "new=$NEW" >> $GITHUB_OUTPUT

- name: Push branch and open PR
env:
GH_TOKEN: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}
run: |
BRANCH="chore/bump-v${{ steps.bump.outputs.new }}"
git push origin --delete "$BRANCH" 2>/dev/null || true
git checkout -b "$BRANCH"
git add packages/schemas/package.json packages/core/package.json packages/react/package.json packages/cli/package.json
git commit -m "Bump version to ${{ steps.bump.outputs.new }}"
git push origin "$BRANCH"
gh pr create \
--title "Bump version to ${{ steps.bump.outputs.new }}" \
--body "Automated version bump from \`${{ steps.bump.outputs.old }}\` to \`${{ steps.bump.outputs.new }}\`. Merging this PR will trigger tag creation and npm publish of all packages." \
--base main \
--head "$BRANCH"
Loading
Loading