rr

star 0

Rahman Resources (rr) operator — context-aware skill for managing vertical slices across the rr monorepo and consumer projects. Detects cwd, runs the right verb. Replaces legacy /rr-prep, /rr-send, /rr-adopt skills (deleted 2026-05-16 with BSDL teardown).

rahmanef63 By rahmanef63 schedule Updated 6/9/2026

name: rr description: Rahman Resources (rr) operator — context-aware skill for managing vertical slices across the rr monorepo and consumer projects. Detects cwd, runs the right verb. Replaces legacy /rr-prep, /rr-send, /rr-adopt skills (deleted 2026-05-16 with BSDL teardown).

/rr — Rahman Resources operator skill

Global skill. Works from ~/projects/resources/ (rr repo) AND from any consumer project. Detects context from cwd, then routes to the right action.

When user invokes

  • /rr — show status of current location (am I in rr or consumer? what's installed?)
  • /rr list — show all available slices in rr catalog
  • /rr info <slug> — full metadata for one slice
  • /rr add <slug> — install slice from rr into consumer (or scaffold new in rr)
  • /rr update <slug> — re-pull newer version (overwrite, warn on local edit)
  • /rr lift <slug> — copy mature slice from consumer UP to rr (operator)
  • /rr changelog — append a release entry covering current diff (mandatory before publish)
  • /rr publish — bump CLI/MCP version + push + npm publish (operator, rr only)
  • Natural language: "bawa fitur cv-generator dari CareerPack ke rr", "install command-menu ke project ini", "update audit-log"

Step 1 — detect context

Run first:

pwd
test -d ./packages/cli && test -f ./CLAUDE.md && head -1 ./CLAUDE.md | grep -q "Rahman Resources" && echo "MODE: rr-repo" || echo "MODE: consumer"

Also check for sentinel files:

  • rr-repo mode if cwd contains: packages/cli/, frontend/slices/, CLAUDE.md starts with "# CLAUDE.md — Rahman Resources"
  • consumer mode if cwd contains: slices/ (no frontend/ prefix) OR frontend/slices/ but NO packages/cli/
  • unknown otherwise — ask user which mode

Show banner: ▸ /rr running in [rr-repo|consumer] mode — cwd=<path>

Step 2 — route by verb

Verb: (none) — show status

rr-repo mode:

ls frontend/slices/ | grep -v _ | wc -l   # how many slices
grep -c "slug:" lib/content/slices.ts     # catalog entries
git log --oneline -5                      # recent activity
node packages/cli/scripts/validate-slice-parity.mjs 2>&1 | tail -3

consumer mode:

ls slices/ 2>/dev/null | head -20         # installed slices
ls shared/ 2>/dev/null | head -10         # cascaded shared

Verb: list

npx rahman-resources list slices

(Or, if rr-repo mode and no internet: grep slug: lib/content/slices.ts)

Verb: info

npx rahman-resources info <slug>

Verb: add

consumer mode:

npx rahman-resources add <slug>

Then verify:

ls slices/<slug>/
test -f slices/<slug>/slice.json && echo "✓ installed"

In rr-repo mode add is a no-op — use scaffold-slice instead (see below).

Verb: init

Bootstraps a fresh Next 16 + Convex + Tailwind 4 consumer project. Scaffolds package.json + tsconfig + Tailwind config + rr.json manifest

  • shadcn components.json + auth wiring (no Clerk — @convex-dev/auth).
npx rahman-resources init <app-name>      # alias: npx rr init <app-name>
cd <app-name>
pnpm install
pnpm exec convex dev                       # if Convex backend wanted

Output rr.json is the consumer manifest schema (packages/cli/lib/rr-schema.json) used by every subsequent rr add / rr update call.

Operator-only when scaffolding for someone else; otherwise users run themselves.

Verb: scaffold-slice (rr-repo mode only)

Creates a new slice skeleton under frontend/slices/<slug>/ with the canonical layout (slice.json + slice.contract.ts + slice.manifest.json

  • config.ts + index.ts + agent.md + components/ + lib/). Wires the new slug into lib/content/slices.ts as a TODO entry the operator fills in. Skips the cp-r ritual when the slice is greenfield (rare — prefer lift from a consumer when source exists).
npx rahman-resources scaffold-slice <slug>

Pre-flight: pick a category (ui / data / auth / infra / ai / content / payment / email / realtime / search). Decide kind: ui (no Convex), full (Convex + UI), convex (backend only).

After scaffolding:

  1. Fill in slice.json description + title + tags
  2. Write slice.contract.ts with defineSliceContract({...})
  3. Add SliceEntry to lib/content/slices.ts (REQUIRED — npm run audit:slices gates)
  4. Append changelog entry in lib/content/changelog.ts
  5. Run node scripts/features/gen-slice-agent-md.mjs + node packages/cli/scripts/gen-manifest.mjs
  6. Verify: npm run validate:all

Verb: update

consumer mode:

npx rahman-resources update <slug>

If conflicts (local edits), CLI warns. Operator decides: keep local OR overwrite.

Verb: lift (consumer → rr)

ONLY from consumer mode. Promotes a mature slice from current consumer up to rr.

Pre-flight checklist (do BEFORE running):

  1. Slice has no Clerk imports
  2. Slice files all ≤200 LOC (audit-file-size hard gate)
  3. No hardcoded consumer-specific terms (project name, business strings)
  4. No cross-slice imports (must use @/components/ui/*, @/shared/*, or @/features/<own-slug>/*) — @/components/templates/_shared/* is an explicit cross-slice peer prefix allowed for slices that depend on template-shared primitives (CRUD, landing-sections, pages)
  5. Convex tables (if any) prefixed <slug>_* with by_workspace index

Then:

# 1. Manual copy (the BSDL pipeline was torn down 2026-05-16; manual is the way)
SRC=$(pwd)/slices/<slug>           # or frontend/slices/<slug> per consumer convention
DST=~/projects/resources/frontend/slices/<slug>
cp -r "$SRC" "$DST"

# 2. Rewrite imports in rr-side copy if needed
# Example: replace consumer-specific aliases with rr-standard
# (do this with sed or Edit tool per file)

# 3. Add metadata if missing
cd ~/projects/resources/frontend/slices/<slug>
# Required: slice.json (slug, version, deps, paths)
# Recommended: slice.contract.ts (typed DSL)
# For CLI distribution: slice.manifest.json

# 4. Validate
cd ~/projects/resources
npm run audit:slices
npm run validate:all

# 5. Update catalog
# Edit lib/content/slices.ts — add SliceEntry for <slug>
# Regenerate manifest (run from repo root — same path used everywhere else)
node packages/cli/scripts/gen-manifest.mjs

# 6. MANDATORY — append changelog entry (see Changelog discipline below)
# Edit lib/content/changelog.ts — add a release entry with bullet
# referencing { text: "<slug> — lifted from <consumer>", slug: "<slug>" }

# 7. Commit + push
git add frontend/slices/<slug> lib/content/slices.ts \
        packages/cli/lib/manifest.json lib/content/changelog.ts
git commit -m "feat(<slug>): lift from <consumer-name>"
git push origin main

If user wants the lift published as a new CLI version, also bump version (see publish verb).

Verb: changelog (rr-repo mode)

MANDATORY before publish. Every wave/ship that touches a slice or template needs an entry so consumers and the /changelog page can see what changed and <RecentlyUpdatedBadge> lights up on detail pages.

Open lib/content/changelog.ts and prepend a new ChangelogEntry:

{
  id: "<wave-or-version>",          // anchor target — must match /changelog#<id>
  version: "<wave-or-version>",
  date: Date.parse("YYYY-MM-DD"),
  kind: "feature" | "improvement" | "fix" | "chore" | "breaking",
  title: "Short one-line summary",
  body: "Why-not-what paragraph. Cover the spirit of the wave.",
  groups: [
    {
      heading: "Slices touched",
      bullets: [
        { text: "<slug> — what changed", slug: "<slug>" },
      ],
    },
    {
      heading: "Templates touched",
      bullets: [
        { text: "<slug> — what changed", slug: "<slug>", kind: "template" },
      ],
    },
    {
      heading: "Site",
      bullets: [
        "Plain string for non-catalog notes (e.g. new helper added)",
      ],
    },
  ],
},

Schema rules:

  • Bullet { text, slug } (no kind field) defaults to kind: "slice" → links to /slices/<slug>. Set kind: "template" for layouts.
  • href field can override the generated URL entirely (rare — for external docs links).
  • Plain strings still work for items that don't map to a catalog entry.
  • The id field becomes the <RecentlyUpdatedBadge> deep-link target via /changelog#<id> (scroll-mt-24 already wired).

After editing, verify with the repo's real gate (don't hand-craft grep -v filters for slices that may not exist):

npm run validate:all       # tsc + slice/contract/manifest audits

Verb: publish (rr-repo mode only)

Operator-only. Bumps + publishes CLI and/or MCP to npm.

Before publishing — ensure the changelog entry exists for the versions being published. CI does NOT enforce this yet but the <RecentlyUpdatedBadge> will be wrong if you skip it.

⚠ prepublishOnly gate — TS↔slice.json parity (learned 2026-05-28). packages/cli's prepublishOnly runs sync-skills --check + validate + validate-slice --check + validate-slice-parity + validate-structure. npm publish ABORTS on the first failure, so a drift you didn't cause can block your publish. The two that bite:

  • validate-slice — every frontend/slices/*/slice.json must satisfy the schema. Common break: deps.peers must be [{slug,range,reason}] objects, NOT bare strings (peers: ["seo"] → fails).
  • validate-slice-parity — each slice's version + title in lib/content/slices.ts (TS) must EXACTLY match its slice.json. A version bump in one but not the other, or an em-dash vs slash title mismatch, fails the publish.

Pre-flight (run BEFORE asking the user for the OTP — fix any drift, commit, push, THEN publish):

cd packages/cli
node scripts/sync-skills.mjs --check && node scripts/validate.mjs \
  && node scripts/validate-slice.mjs --check \
  && node scripts/validate-slice-parity.mjs \
  && node scripts/validate-structure.mjs
echo "EXIT=$?"   # must be 0 before publish

If another agent's in-progress slice (uncommitted WIP) trips the gate, set it aside (stash the slices.ts entry + mv the untracked dirs to /tmp), publish clean, then restore byte-for-byte — never publish a manifest entry whose slice files aren't yet on main (npx rr add <that-slug> would break for users).

# CLI
cd packages/cli
npm version <patch|minor|major> --no-git-tag-version
cd ../..
git add packages/cli/package.json && git commit -m "chore(cli): bump <new-version>"
git push origin main
cd packages/cli && npm publish --otp=...

# MCP (similar)
cd packages/mcp
npm version <patch|minor|major> --no-git-tag-version
cd ../..
git add packages/mcp/package.json && git commit -m "chore(mcp): bump <new-version>"
git push origin main
cd packages/mcp && npm publish --otp=...

User runs OTP step. Never run npm publish autonomously.

Version bump rules:

  • patch — bugfix only, no API change
  • minor — additive feature
  • major — breaking: removed command/URI/flag, changed default behavior

Changelog discipline (always apply)

Every PR/wave/lift that touches a catalog item MUST append a ChangelogEntry in lib/content/changelog.ts BEFORE the commit lands on main.

Why:

  • <RecentlyUpdatedBadge> on /slices/<slug> and /layouts/<slug> reads lib/content/changelog-helpers.ts::getLatestUpdate(slug, kind) and shows "Updated 3d ago" → links to /changelog#<releaseId>.
  • Without an entry, consumers can't tell what's fresh — they lose track of which slices/templates just got polished.

Pattern — one entry per ship, even tiny ones:

{
  id: "AO",                                // wave letter or version
  version: "AO-wave",
  date: Date.parse("2026-05-19"),
  kind: "fix",
  title: "Audit-log time formatter rounded down off-by-one",
  body: "Fix only — no API change.",
  groups: [{
    heading: "Slices touched",
    bullets: [
      { text: "audit-log — fmtRelative now uses Math.floor", slug: "audit-log" },
    ],
  }],
},

Quick check (before push):

grep "date: Date.parse" lib/content/changelog.ts | head -3   # newest first?
node packages/cli/scripts/gen-manifest.mjs                    # manifest still clean
npx tsc --noEmit 2>&1 | head                                  # types still happy

Live preview SSOT (when adding new slices/templates with previews)

Both /slices/[slug] and /layouts/[slug] use the same docs-shell tabbed UI (Code / Public / Split / Admin / Prompt). Single helper:

import { buildPreviewManifest } from "@/components/site/preview";

const manifest = buildPreviewManifest({
  title, subtitle,
  publicPath, adminPath,           // tabs conditional on presence
  defaultSurface, defaultView, defaultZoom,
  code: () => <CodeTab ... />,     // your code tab body
  prompt: () => <PromptTab ... />, // optional
  extras: [...],                   // additional tabs
  inspector, sourceRepo,
  config, composePrompt, composePreviewSrc,
});
useFeatureManifest(manifest);

When you ship a new slice with both previewPath + adminPreviewPath in lib/content/slices.ts, the tabbed shell renders automatically — no per-page wiring needed.

Landing-sections schema (the canonical landing CRUD)

The landing-sections slice (also lives at components/templates/_shared/landing/ for rr-internal use) is the SSOT for admin-editable landing pages.

LandingSection fields:

  • id, kind (hero/features/pricing/blog/changelog/faq/portfolio/services/stats/newsletter/cta/testimonials/custom)
  • title, subtitle
  • order (1-based; list has up/down arrows)
  • enabled
  • imageUrl + imageRatio (16:9 default, dropdown)
  • bgImageUrl (auto soft scrim for readability)
  • className (custom Tailwind appended to section wrapper)
  • config (JSON for kind-specific extras: {badge}, {columns}, {limit})

DRY editor: every field defined ONCE in components/templates/_shared/landing/landing-fields.ts::LANDING_FIELDS. Both list-dialog and full-page editor consume it.

Per-template renderer pattern (every template's slices/home/LandingRenderer.tsx):

case "hero":
  return (
    <LandingSectionShell section={section}>
      <Hero ... image={section.imageUrl ? { url: section.imageUrl, ratio: section.imageRatio } : undefined} />
    </LandingSectionShell>
  );

When adding a new template: copy the pattern from any of the 7 existing templates. Make sure state.landingSections is in your store with sensible seed kinds (hero + features + cta minimum).

VersionWatcher (client redeploy detection)

components/system/VersionWatcher.tsx polls /api/version every 5min

  • on focus/visibility. When deployed BUILD_ID differs from boot NEXT_PUBLIC_BUILD_ID, sonner toast offers "Muat ulang" → hardReload (CacheStorage purge + cache-buster query). Mounted in app/layout.tsx.

next.config.mjs bakes NEXT_PUBLIC_BUILD_ID at build time (env id / deployment id / Date.now fallback).

For new consumer projects: copy components/system/VersionWatcher.tsx

  • app/api/version/route.ts + the next.config snippet. Mount the component near <Toaster />.

Catalog — query live, never hardcode

The catalog drifts every wave, so this skill does NOT embed a slice list (a baked snapshot rots — e.g. it once named database-io and a mentions slice that no longer exist). Always read it live from the SSOT instead:

# Slices + counts (SSOT = lib/content/slices.ts → manifest):
node packages/cli/scripts/gen-manifest.mjs >/dev/null
node -e "const m=require('./packages/cli/lib/manifest.json'); \
  console.log('slices',m.slices.length,'| layouts',m.layouts.length,'| features',m.features.length)"
npx rahman-resources list slices         # human-readable, from anywhere

# Templates (the `*-os` family) + layout snippets:
grep slug: lib/content/layouts.ts
ls components/templates/                  # one dir per website template

Shared template facts (stable, safe to remember):

  • Every *-os template shares landing-sections admin↔public live-sync via BroadcastChannel, <LandingSectionShell> per renderer kind, CRUD via <CrudListView> + <CrudRowDialog> with <FieldDef> schemas, and <RecentlyUpdatedBadge> on the detail header.
  • Layout snippets = standalone JSX recipes at /layouts/<slug> (not full apps).

How other projects/agents should consume rr

When a consumer project sends data here (lift, harvest, sync, agent ping), they need to:

  1. Check the catalog first — run npx rahman-resources list slices OR scan the snapshot above. Don't duplicate an existing slice.
  2. Match the slug — if their slice is conceptually the same as one in rr, use that exact slug. New name only when no overlap.
  3. Pre-flight before lift — run the 5-point checklist above (no Clerk, 200-LOC cap, no consumer-specific strings, allowed imports, Convex prefixed).
  4. Provide changelog text — the lifting operator MUST add an entry to lib/content/changelog.ts naming their slug. Bullet shape: { text: "<slug> — <what-changed-in-1-line>", slug: "<slug>" }. Without this the badge won't show on /slices/.
  5. Mention if there's a peer — if their slice depends on another rr slice (e.g. landing-sections needs CRUD shell), declare it in slice.contract.ts under requires.peers with range + reason.

When AGENTS run against rr from outside:

  • Source of truth for catalog: lib/content/slices.ts (export slices) + lib/content/layouts.ts.
  • Source of truth for changelog: lib/content/changelog.ts (export releases).
  • Helper: lib/content/changelog-helpers.ts::getLatestUpdate(slug, kind) → returns latest entry referencing the slug.
  • Recently-shipped lookup: isRecentlyUpdated(slug, kind, windowDays=14).

Source map per consumer (for lift verb)

Slice directory differs slightly per consumer. Detect from cwd:

Consumer Slice base Notes
superspace frontend/slices/ Largest (51 slices) — mostly business-locked
notion-page-clone frontend/slices/ 35 slices — biggest harvest opportunity
rahmanef.com frontend/slices/ 26 slices — many portable UI primitives
content-rahmanef-com frontend/slices/ 11 slices, pure rr consumer
CareerPack frontend/src/slices/ Note: extra src/ segment
cescadesigns (none yet) Repo not yet sliced — defer harvest
rc-samata-dash src/shared/components/ VersionWatcher pattern source

Hard rules (always apply, never skip)

  1. No Clerk — auth = @convex-dev/auth (rr mandate)
  2. shadcn-only UI — raw <button>, <dialog>, native <input type=date|file> forbidden
  3. Copy-first, never greenfield — for new slice, always start from cp -r of source
  4. Solo dev → push direct to main, never open PR (see global rule in ~/.claude/CLAUDE.md)
  5. No .kitab.json — BSDL is dead. If you see .kitab.json in any project, delete it.
  6. No term "kitab" in new code/docs — say "rr" or "Rahman Resources"
  7. 200-LOC file cap — audit-file-size hard gate. Refactor into sub-files when crossing.
  8. bare .collect() forbidden in Convex queries — use .withIndex(...).take(N).
  9. Public Convex fn MUST have args validator — audit-bp P0.
  10. Changelog entry required when touching a catalog item before push (see Changelog discipline above).

Output format

Always end with caveman bahasa Indonesia recap (per global feedback):

  • What changed
  • Where to look
  • What NOT to expect

Compat note

This skill REPLACES three deleted skills (removed 2026-05-16 with BSDL teardown):

  • /rr-prep (was: preflight slice for harvest) → now folded into lift verb pre-flight checklist
  • /rr-send (was: BSDL bidir send) → now folded into lift verb (manual cp-r flow)
  • /rr-adopt (was: migrate to rahman-shared npm) → now: if user wants, just pnpm add rahman-shared directly, no skill needed
Install via CLI
npx skills add https://github.com/rahmanef63/resource-site --skill rr
Repository Details
star Stars 0
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator