diff --git a/.brv/context-tree/design/caplets/caplets_module_findings.md b/.brv/context-tree/design/caplets/caplets_module_findings.md new file mode 100644 index 0000000..8031dd6 --- /dev/null +++ b/.brv/context-tree/design/caplets/caplets_module_findings.md @@ -0,0 +1,106 @@ +--- +title: Caplets Module Findings +summary: Extracted and organized factual statements about the caplets module +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:45:38.837Z' +updatedAt: '2026-05-28T16:45:38.837Z' +--- +## Reason +Curate extracted facts from caplets module context + +## Raw Concept +**Task:** +Document caplets module findings + +**Timestamp:** 2026-05-28T16:45:38.834Z + +## Narrative +### Structure +Aggregated facts from caplets module extraction + +### Highlights +PRODUCT.md, DESIGN.md, context loader, IMPECCABLE_CONTEXT_DIR, load-context.mjs, loader output, loader execution, context reload, live.mjs, craft task, design register, register identification, register inference, reference files, shared design laws, color model, hex colors, neutral tinting, design strategy, theme choice, line length, hierarchy, spacing, cards usage, nested cards, containers, animation, easing, side-stripe borders, gradient text, glassmorphism, hero-metric template, identical card grids, modal usage, copy, punctuation, failures, Category-reflex check, First-order reflex, Second-order reflex, `craft` command, `shape` command, `teach` command, `document` command, `extract` command, `critique` command, `audit` command, `polish` command, `bolder` command, `quieter` command, `distill` command, `harden` command, `onboard` command, `animate` command, `colorize` command, `typeset` command, `layout` command, `delight` command, `overdrive` command, `clarify` command, `adapt` command, `optimize` command, `live` command, management commands, Pin command, Unpin command, pin.mjs script, landing page update, index.astro, proof data, build process, pnpm format:check, pnpm typecheck, pnpm build, pnpm verify + +## Facts +- **PRODUCT.md**: PRODUCT.md is required and contains users, brand, tone, anti-references, and strategic principles. +- **DESIGN.md**: DESIGN.md is optional but strongly recommended and includes colors, typography, elevation, and components. +- **context loader**: The loader looks at the project root by default and falls back to .agents/context/ and docs/ if the root is clean. +- **IMPECCABLE_CONTEXT_DIR**: The context directory can be overridden with IMPECCABLE_CONTEXT_DIR=path/to/dir (absolute or relative to cwd). +- **load-context.mjs**: Both PRODUCT.md and DESIGN.md can be loaded in one call using node {{scripts_path}}/load-context.mjs. +- **loader output**: The output of the loader must not be piped through head, tail, grep, or jq. +- **loader execution**: If the loader output is already present in the session history, it should not be re-run. +- **context reload**: A fresh load is required after running {{command_prefix}}impeccable teach, {{command_prefix}}impeccable document, or when the user manually edits a file. +- **live.mjs**: If {{command_prefix}}impeccable live has been run, do not also run load-context.mjs in the same session. +- **PRODUCT.md**: If PRODUCT.md is missing, empty, or contains only placeholder [TODO] markers (<200 characters), run {{command_prefix}}impeccable teach and then resume the original task with fresh context. +- **craft task**: If the original task was {{command_prefix}}impeccable craft, resume into {{command_prefix}}impeccable shape before any implementation work after teaching. +- **DESIGN.md**: If DESIGN.md is missing, prompt the user once per session to run {{command_prefix}}impeccable document for more on‑brand output, then continue. +- **design register**: Every design task is classified as either brand (marketing, landing, campaign, long‑form content, portfolio) or product (app UI, admin, dashboard, tool). +- **register identification**: Design registration should be identified before designing, using priority: task cue, surface in focus, then register field in PRODUCT.md. +- **register inference**: If PRODUCT.md lacks a register field, infer it from the Users and Product Purpose sections, cache it for the session, and suggest running {{command_prefix}}impeccable teach to add the field explicitly. +- **reference files**: Load the matching reference file: reference/brand.md for brand tasks or reference/product.md for product tasks. +- **shared design laws**: Shared design laws apply to all designs; match implementation complexity to aesthetic vision—maximalism requires elaborate code, minimalism requires precision. +- **color model**: Use OKLCH. +- **hex colors**: Never use #000 or #fff. +- **neutral tinting**: Tint every neutral toward the brand hue (chroma 0.005–0.01 is enough). +- **design strategy**: Restrained: tinted neutrals + one accent ≤10%. +- **design strategy**: Committed: one saturated color carries 30–60% of the surface. +- **design strategy**: Full palette: 3–4 named roles, each used deliberately. +- **design strategy**: Drenched: the surface IS the color. +- **theme choice**: Dark vs. light is never a default. +- **line length**: Cap body line length at 65–75ch. +- **hierarchy**: Hierarchy through scale + weight contrast (≥1.25 ratio between steps). +- **spacing**: Vary spacing for rhythm. +- **cards usage**: Cards are the lazy answer. Use them only when they're truly the best affordance. +- **nested cards**: Nested cards are always wrong. +- **containers**: Don't wrap everything in a container. +- **animation**: Don't animate CSS layout properties. +- **easing**: Ease out with exponential curves (ease-out-quart / quint / expo). No bounce, no elastic. +- **side-stripe borders**: Side-stripe borders greater than 1px as a colored accent are never intentional. +- **gradient text**: Gradient text using background-clip: text combined with a gradient background is decorative, never meaningful. +- **glassmorphism**: Glassmorphism as default is banned. +- **hero-metric template**: The hero-metric template is a SaaS cliché. +- **identical card grids**: Identical card grids are banned. +- **modal usage**: Modal as first thought is usually laziness. +- **copy**: Every word earns its place. +- **punctuation**: No em dashes; use commas, colons, semicolons, periods, or parentheses. +- **failures**: Register-specific failures live in each reference. +- **Category-reflex check**: Category-reflex check runs at two altitudes; the second one catches what the first one misses. +- **First-order reflex**: First-order reflex occurs if someone could guess the theme and palette from the category alone. +- **Second-order reflex**: Second-order reflex occurs if someone could guess the aesthetic family from category-plus-anti-references. +- **`craft` command**: `craft [feature]` shapes and builds a feature end-to-end. +- **`shape` command**: `shape [feature]` plans UX/UI before writing code. +- **`teach` command**: `teach` sets up PRODUCT.md and DESIGN.md context. +- **`document` command**: `document` generates DESIGN.md from existing project code. +- **`extract` command**: `extract [target]` pulls reusable tokens and components into design system. +- **`critique` command**: `critique [target]` performs UX design review with heuristic scoring. +- **`audit` command**: `audit [target]` conducts technical quality checks such as accessibility, performance, and responsiveness. +- **`polish` command**: `polish [target]` provides a final quality pass before shipping. +- **`bolder` command**: `bolder [target]` amplifies safe or bland designs. +- **`quieter` command**: `quieter [target]` tones down aggressive or overstimulating designs. +- **`distill` command**: `distill [target]` strips to essence and removes complexity. +- **`harden` command**: `harden [target]` makes designs production-ready, handling errors, i18n, and edge cases. +- **`onboard` command**: `onboard [target]` designs first-run flows, empty states, and activation. +- **`animate` command**: `animate [target]` adds purposeful animations and motion. +- **`colorize` command**: `colorize [target]` adds strategic color to monochromatic UIs. +- **`typeset` command**: `typeset [target]` improves typography hierarchy and fonts. +- **`layout` command**: `layout [target]` fixes spacing, rhythm, and visual hierarchy. +- **`delight` command**: `delight [target]` adds personality and memorable touches. +- **`overdrive` command**: `overdrive [target]` pushes past conventional limits. +- **`clarify` command**: `clarify [target]` improves UX copy, labels, and error messages. +- **`adapt` command**: `adapt [target]` adapts designs for different devices and screen sizes. +- **`optimize` command**: `optimize [target]` diagnoses and fixes UI performance issues. +- **`live` command**: `live` enables visual variant mode to pick elements in the browser and generate alternatives. +- **management commands**: Two management commands are `pin ` and `unpin `. +- **Pin command**: Pin creates a standalone shortcut so `{{command_prefix}}` invokes `{{command_prefix}}impeccable ` directly. +- **Unpin command**: Unpin removes the shortcut created by Pin. +- **pin.mjs script**: The script writes to every harness directory present in the project. +- **landing page update**: Clarified `apps/landing/` copy and pushed it to PR #93. +- **index.astro**: Replaced remaining “backend” phrasing with clearer “tool source” language in `apps/landing/src/pages/index.astro`. +- **proof data**: Renamed the proof data from `skillifyFramework` to `capabilityFramework`. +- **build process**: Verification commands `pnpm format:check`, `pnpm typecheck`, `pnpm build`, and `pnpm verify` all passed. +- **pnpm format:check**: `pnpm format:check -- apps/landing/src/pages/index.astro` passed +- **pnpm typecheck**: `pnpm --filter @caplets/landing typecheck` passed +- **pnpm build**: `pnpm --filter @caplets/landing build` passed +- **pnpm verify**: `pnpm verify` passed diff --git a/.brv/context-tree/design/caplets/formatting_check.md b/.brv/context-tree/design/caplets/formatting_check.md new file mode 100644 index 0000000..36b2c26 --- /dev/null +++ b/.brv/context-tree/design/caplets/formatting_check.md @@ -0,0 +1,22 @@ +--- +title: Formatting check +summary: Facts about formatting check +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.789Z' +updatedAt: '2026-05-28T16:03:02.789Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about formatting check + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **formatting check**: Ran formatting check successfully: pnpm format:check -- README.md. diff --git a/.brv/context-tree/design/caplets/hero_copy.md b/.brv/context-tree/design/caplets/hero_copy.md new file mode 100644 index 0000000..e474421 --- /dev/null +++ b/.brv/context-tree/design/caplets/hero_copy.md @@ -0,0 +1,22 @@ +--- +title: Hero copy +summary: Facts about hero copy +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.759Z' +updatedAt: '2026-05-28T16:03:02.759Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about hero copy + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **hero copy**: Updated hero copy to “Skillify your backends.” diff --git a/.brv/context-tree/design/caplets/landing_positioning.md b/.brv/context-tree/design/caplets/landing_positioning.md new file mode 100644 index 0000000..037a755 --- /dev/null +++ b/.brv/context-tree/design/caplets/landing_positioning.md @@ -0,0 +1,22 @@ +--- +title: Landing positioning +summary: Facts about landing positioning +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.763Z' +updatedAt: '2026-05-28T16:03:02.763Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about landing positioning + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **landing positioning**: Matched landing positioning around staged discovery: one card, searchable tools, inspectable schemas, preserved results. diff --git a/.brv/context-tree/design/caplets/quick_start.md b/.brv/context-tree/design/caplets/quick_start.md new file mode 100644 index 0000000..dd3350d --- /dev/null +++ b/.brv/context-tree/design/caplets/quick_start.md @@ -0,0 +1,22 @@ +--- +title: Quick Start +summary: Facts about Quick Start +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.770Z' +updatedAt: '2026-05-28T16:03:02.770Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about Quick Start + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **Quick Start**: Updated Quick Start to match landing install flow using npm install -g caplets. diff --git a/.brv/context-tree/design/caplets/readme_header_badges.md b/.brv/context-tree/design/caplets/readme_header_badges.md new file mode 100644 index 0000000..b5c1c49 --- /dev/null +++ b/.brv/context-tree/design/caplets/readme_header_badges.md @@ -0,0 +1,22 @@ +--- +title: README header badges +summary: Facts about README header badges +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.755Z' +updatedAt: '2026-05-28T16:03:02.755Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about README header badges + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **README header badges**: Added https://caplets.dev near the README header badges. diff --git a/.brv/context-tree/design/caplets/readme_md.md b/.brv/context-tree/design/caplets/readme_md.md new file mode 100644 index 0000000..41f16c8 --- /dev/null +++ b/.brv/context-tree/design/caplets/readme_md.md @@ -0,0 +1,22 @@ +--- +title: README.md +summary: Facts about README.md +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.747Z' +updatedAt: '2026-05-28T16:03:02.747Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about README.md + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **README.md**: Updated README.md to align with apps/landing/ messaging and added the landing page link. diff --git a/.brv/context-tree/design/caplets/readme_section.md b/.brv/context-tree/design/caplets/readme_section.md new file mode 100644 index 0000000..513dae2 --- /dev/null +++ b/.brv/context-tree/design/caplets/readme_section.md @@ -0,0 +1,22 @@ +--- +title: README section +summary: Facts about README section +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:03:02.773Z' +updatedAt: '2026-05-28T16:03:02.773Z' +--- +## Reason +Curate extracted facts from context + +## Raw Concept +**Task:** +Document facts about README section + +## Narrative +### Highlights +Extracted 1 facts. + +## Facts +- **README section**: Added “Trust Before Invocation” section based on landing content. diff --git a/.brv/context-tree/facts/ci/ci.md b/.brv/context-tree/facts/ci/ci.md new file mode 100644 index 0000000..1d35d63 --- /dev/null +++ b/.brv/context-tree/facts/ci/ci.md @@ -0,0 +1,20 @@ +--- +title: CI +summary: Facts about CI +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.986Z' +updatedAt: '2026-05-28T16:21:51.986Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for CI + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **CI**: CI was still running when checked. diff --git a/.brv/context-tree/facts/extracted_facts/extracted_facts.md b/.brv/context-tree/facts/extracted_facts/extracted_facts.md new file mode 100644 index 0000000..e653bd8 --- /dev/null +++ b/.brv/context-tree/facts/extracted_facts/extracted_facts.md @@ -0,0 +1,24 @@ +--- +title: extracted_facts +summary: Aggregated factual statements from user conversation +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:46:02.415Z' +updatedAt: '2026-05-28T16:46:02.415Z' +--- +## Reason +Curate extracted facts from RLM context + +## Raw Concept +**Task:** +Curate extracted factual statements from conversation + +**Flow:** +extraction -> dedup -> upsert + +**Timestamp:** 2026-05-28T16:46:02.411Z + +## Narrative +### Structure +Aggregated factual statements grouped by subject diff --git a/.brv/context-tree/facts/mcp_example/mcp_example.md b/.brv/context-tree/facts/mcp_example/mcp_example.md new file mode 100644 index 0000000..3ef7628 --- /dev/null +++ b/.brv/context-tree/facts/mcp_example/mcp_example.md @@ -0,0 +1,20 @@ +--- +title: MCP example +summary: Facts about MCP example +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.966Z' +updatedAt: '2026-05-28T16:21:51.966Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for MCP example + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **MCP example**: Moved that MCP example into the optional backend examples section. diff --git a/.brv/context-tree/facts/minimal_quick_start/minimal_quick_start.md b/.brv/context-tree/facts/minimal_quick_start/minimal_quick_start.md new file mode 100644 index 0000000..609c19f --- /dev/null +++ b/.brv/context-tree/facts/minimal_quick_start/minimal_quick_start.md @@ -0,0 +1,20 @@ +--- +title: Minimal Quick Start +summary: Facts about minimal Quick Start +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.964Z' +updatedAt: '2026-05-28T16:21:51.964Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for minimal Quick Start + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **minimal Quick Start**: Removed the optional Context7 `caplets add mcp docs ...` command from the minimal Quick Start. diff --git a/.brv/context-tree/facts/pnpm_format_check/pnpm_format_check.md b/.brv/context-tree/facts/pnpm_format_check/pnpm_format_check.md new file mode 100644 index 0000000..8d932ef --- /dev/null +++ b/.brv/context-tree/facts/pnpm_format_check/pnpm_format_check.md @@ -0,0 +1,20 @@ +--- +title: Pnpm format check +summary: Facts about pnpm format check +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.969Z' +updatedAt: '2026-05-28T16:21:51.969Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for pnpm format check + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **pnpm format check**: `pnpm format:check -- README.md` passed. diff --git a/.brv/context-tree/facts/pnpm_verify/pnpm_verify.md b/.brv/context-tree/facts/pnpm_verify/pnpm_verify.md new file mode 100644 index 0000000..42cab7f --- /dev/null +++ b/.brv/context-tree/facts/pnpm_verify/pnpm_verify.md @@ -0,0 +1,20 @@ +--- +title: Pnpm verify +summary: Facts about pnpm verify +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.971Z' +updatedAt: '2026-05-28T16:21:51.971Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for pnpm verify + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **pnpm verify**: `pnpm verify` passed. diff --git a/.brv/context-tree/facts/pr_93/pr_93.md b/.brv/context-tree/facts/pr_93/pr_93.md new file mode 100644 index 0000000..17be082 --- /dev/null +++ b/.brv/context-tree/facts/pr_93/pr_93.md @@ -0,0 +1,20 @@ +--- +title: 'PR #93' +summary: 'Facts about PR #93' +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.960Z' +updatedAt: '2026-05-28T16:21:51.960Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for PR #93 + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **PR #93**: Addressed PR review feedback and pushed updates to PR #93. diff --git a/.brv/context-tree/facts/project/179803b.md b/.brv/context-tree/facts/project/179803b.md new file mode 100644 index 0000000..01b6c55 --- /dev/null +++ b/.brv/context-tree/facts/project/179803b.md @@ -0,0 +1,20 @@ +--- +title: 179803b +summary: Facts about 179803b +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:59:16.797Z' +updatedAt: '2026-05-28T16:59:16.797Z' +--- +## Reason +Curated facts extracted from context + +## Raw Concept +**Task:** +Document facts for 179803b + +**Timestamp:** 2026-05-28T16:59:16.794Z + +## Facts +- **179803b**: Commit 179803b docs(agents): byterover context was automatically created by the new post-commit hook for .brv/ changes diff --git a/.brv/context-tree/facts/project/ab9efe5.md b/.brv/context-tree/facts/project/ab9efe5.md new file mode 100644 index 0000000..dfc0c48 --- /dev/null +++ b/.brv/context-tree/facts/project/ab9efe5.md @@ -0,0 +1,20 @@ +--- +title: ab9efe5 +summary: Facts about ab9efe5 +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:59:16.795Z' +updatedAt: '2026-05-28T16:59:16.795Z' +--- +## Reason +Curated facts extracted from context + +## Raw Concept +**Task:** +Document facts for ab9efe5 + +**Timestamp:** 2026-05-28T16:59:16.794Z + +## Facts +- **ab9efe5**: Commit ab9efe5 feat(agents): commit byterover context from hook implements scripts/commit-byterover-context.ts, adds tests in scripts/commit-byterover-context.test.ts, adds warn-only .brv/ check to .husky/pre-push, and includes .husky/post-commit diff --git a/.brv/context-tree/facts/project/extracted_facts.md b/.brv/context-tree/facts/project/extracted_facts.md new file mode 100644 index 0000000..86187c0 --- /dev/null +++ b/.brv/context-tree/facts/project/extracted_facts.md @@ -0,0 +1,26 @@ +--- +title: Extracted Facts +summary: Curated factual statements extracted from source context +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:47:59.662Z' +updatedAt: '2026-05-28T16:47:59.662Z' +--- +## Reason +Store extracted factual statements from context + +## Raw Concept +**Task:** +Curate extracted factual statements from provided context + +**Timestamp:** 2026-05-28T16:47:59.660Z + +## Facts +- **changesets**: Changesets supports excluding packages via `.changeset/config.json`. +- **config**: Added "ignore": ["@caplets/landing"] to the config. +- **changeset status**: `pnpm changeset status --since=origin/main` now passes with no changeset needed for landing-only changes. +- **PR**: Pushed to PR #93 in commit `3f21287 chore: ignore landing app in changesets`. +- **changeset status**: `pnpm changeset status --since=origin/main` passed. +- **format check**: `pnpm format:check -- .changeset/config.json` passed. +- **verification**: `pnpm verify` passed. diff --git a/.brv/context-tree/facts/project/post_commit_hook_brv.md b/.brv/context-tree/facts/project/post_commit_hook_brv.md new file mode 100644 index 0000000..8520625 --- /dev/null +++ b/.brv/context-tree/facts/project/post_commit_hook_brv.md @@ -0,0 +1,41 @@ +--- +title: post_commit_hook_brv +summary: Post-commit hook auto-commits .brv context and pre-push verifies no uncommitted .brv changes +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:56:48.398Z' +updatedAt: '2026-05-28T16:56:48.398Z' +--- +## Reason +Document design of post-commit hook to commit .brv changes and pre-push check + +## Raw Concept +**Task:** +Implement git hooks for automatic .brv context commits and verification before push + +**Files:** +- .husky/post-commit +- .husky/pre-push +- scripts/commit-byterover-context.ts + +**Flow:** +post-commit -> detect .brv changes -> stage .brv -> commit with docs(agents): byterover context; pre-push -> verify .brv clean before push + +**Timestamp:** 2026-05-28T16:56:15.853Z + +**Author:** assistant + +## Narrative +### Structure +post-commit runs tsx script; script checks git status, stages .brv, makes conventional commit; pre-push runs verify then ensures .brv clean + +### Highlights +Avoid recursion via BYTEROVER_CONTEXT_COMMIT env var; fail push if .brv has uncommitted changes + +## Facts +- **husky_post_commit**: .husky/post-commit runs pnpm exec tsx ./scripts/commit-byterover-context.ts [project] +- **husky_pre_push**: .husky/pre-push runs pnpm verify [project] +- **script_detection**: commit-byterover-context.ts detects .brv changes via git status --porcelain -- .brv [project] +- **script_commit**: Script stages .brv and creates docs(agents): byterover context commit [project] +- **pre_push_check**: Pre-push check fails if .brv has uncommitted changes [project] diff --git a/.brv/context-tree/facts/project/uncategorized.md b/.brv/context-tree/facts/project/uncategorized.md new file mode 100644 index 0000000..ed64ded --- /dev/null +++ b/.brv/context-tree/facts/project/uncategorized.md @@ -0,0 +1,25 @@ +--- +title: uncategorized +summary: Facts about uncategorized +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:59:16.799Z' +updatedAt: '2026-05-28T16:59:16.799Z' +--- +## Reason +Curated facts extracted from context + +## Raw Concept +**Task:** +Document facts for uncategorized + +**Timestamp:** 2026-05-28T16:59:16.794Z + +## Facts +- pnpm test scripts/commit-byterover-context.test.ts passed with 5 tests +- pnpm format:check passed +- pnpm lint passed +- pnpm typecheck passed +- Post-commit behavior now commits .brv/ changes as docs(agents): byterover context +- Pre-push behavior now runs pnpm verify and then warns about uncommitted .brv/ changes without blocking the push diff --git a/.brv/context-tree/facts/review_threads/review_threads.md b/.brv/context-tree/facts/review_threads/review_threads.md new file mode 100644 index 0000000..02669a3 --- /dev/null +++ b/.brv/context-tree/facts/review_threads/review_threads.md @@ -0,0 +1,20 @@ +--- +title: Review threads +summary: Facts about review threads +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.983Z' +updatedAt: '2026-05-28T16:21:51.983Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for review threads + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **review threads**: Replied to both inline review threads and added a PR summary comment. diff --git a/.brv/context-tree/facts/staged_path_example/staged_path_example.md b/.brv/context-tree/facts/staged_path_example/staged_path_example.md new file mode 100644 index 0000000..6b82499 --- /dev/null +++ b/.brv/context-tree/facts/staged_path_example/staged_path_example.md @@ -0,0 +1,20 @@ +--- +title: Staged-path example +summary: Facts about staged-path example +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:21:51.967Z' +updatedAt: '2026-05-28T16:21:51.967Z' +--- +## Reason +Curated factual statements extracted from PR review context + +## Raw Concept +**Task:** +Document facts for staged-path example + +**Timestamp:** 2026-05-28T16:21:51.958Z + +## Facts +- **staged-path example**: Changed the staged-path example from `github` to `GitHub`. diff --git a/.brv/context-tree/src/caplets/caplets_module_findings.md b/.brv/context-tree/src/caplets/caplets_module_findings.md new file mode 100644 index 0000000..056844e --- /dev/null +++ b/.brv/context-tree/src/caplets/caplets_module_findings.md @@ -0,0 +1,29 @@ +--- +title: Caplets Module Findings +summary: Extracted key facts and narratives from caplets module +tags: [] +related: [] +keywords: [] +createdAt: '2026-05-28T16:06:53.148Z' +updatedAt: '2026-05-28T16:06:53.148Z' +--- +## Reason +Curate extracted facts from caplets source + +## Raw Concept +**Task:** +Document caplets module insights + +**Timestamp:** 2026-05-28T16:06:53.146Z + +## Narrative +### Highlights +The assistant is using the finishing-a-development-branch skill to complete the work | A pull request was opened at https://github.com/spiritledsoftware/caplets/pull/93 | The pull request targets the branch docs/align-readme-with-landing | The commit b37a94c with message "docs: align readme with landing page" was included in the PR | The verification command pnpm verify passed | Files under .brv/context-tree/... remain untracked and were not included in the commit + +## Facts +- **assistant**: The assistant is using the finishing-a-development-branch skill to complete the work +- **PR 93**: A pull request was opened at https://github.com/spiritledsoftware/caplets/pull/93 +- **docs/align-readme-with-landing**: The pull request targets the branch docs/align-readme-with-landing +- **b37a94c**: The commit b37a94c with message "docs: align readme with landing page" was included in the PR +- **pnpm verify**: The verification command pnpm verify passed +- **untracked files**: Files under .brv/context-tree/... remain untracked and were not included in the commit diff --git a/.changeset/config.json b/.changeset/config.json index ec98e35..cd68275 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -7,5 +7,5 @@ "access": "public", "baseBranch": "main", "updateInternalDependencies": "patch", - "ignore": [] + "ignore": ["@caplets/landing"] } diff --git a/.github/workflows/changeset-reminder.yml b/.github/workflows/changeset-reminder.yml deleted file mode 100644 index 9e2e495..0000000 --- a/.github/workflows/changeset-reminder.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: Changeset Reminder - -on: - pull_request: - types: [opened, synchronize, reopened, labeled, unlabeled] - -permissions: - pull-requests: write - contents: read - -jobs: - remind: - name: Remind - runs-on: ubuntu-latest - if: contains(github.event.pull_request.labels.*.name, 'no changeset') == false - steps: - - name: Checkout - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Setup pnpm - uses: pnpm/action-setup@v6 - - - name: Setup Node - uses: actions/setup-node@v6 - with: - node-version: 24 - cache: pnpm - - - name: Install dependencies - run: pnpm install --frozen-lockfile - - - name: Check changeset - run: pnpm changeset status --since=origin/main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a112172..1e61c69 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: changeset: name: Changeset runs-on: ubuntu-latest - if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'no changeset') == false + if: github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, '[no changeset]') == false steps: - name: Checkout uses: actions/checkout@v6 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 7337bc0..0561586 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,8 +1,17 @@ name: Deploy on: - release: - types: [published] + push: + branches: + - main + paths: + - .github/workflows/deploy.yml + - alchemy.run.ts + - apps/** + - package.json + - pnpm-lock.yaml + - pnpm-workspace.yaml + - scripts/alchemy-*.ts workflow_dispatch: permissions: diff --git a/.github/workflows/pr-preview-deploy.yml b/.github/workflows/pr-preview-deploy.yml index b3278b2..d8729ea 100644 --- a/.github/workflows/pr-preview-deploy.yml +++ b/.github/workflows/pr-preview-deploy.yml @@ -4,6 +4,19 @@ on: pull_request: branches: - main + paths: + - .github/workflows/pr-preview-deploy.yml + - alchemy.run.ts + - apps/** + - package.json + - pnpm-lock.yaml + - pnpm-workspace.yaml + - scripts/alchemy-*.ts + types: + - opened + - synchronize + - reopened + - closed permissions: contents: read @@ -36,6 +49,7 @@ jobs: run: pnpm install --frozen-lockfile - name: Deploy preview + if: ${{ github.event.action != 'closed' }} run: pnpm run alchemy:deploy env: ALCHEMY_STAGE: pr-${{ github.event.pull_request.number }} @@ -47,3 +61,17 @@ jobs: GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }} GITHUB_TOKEN: ${{ github.token }} PULL_REQUEST: ${{ github.event.pull_request.number }} + + - name: Destroy preview + if: ${{ github.event.action == 'closed' }} + run: pnpm run alchemy:destroy + env: + ALCHEMY_STAGE: pr-${{ github.event.pull_request.number }} + ALCHEMY_PASSWORD: ${{ secrets.ALCHEMY_PASSWORD }} + ALCHEMY_STATE_TOKEN: ${{ secrets.ALCHEMY_STATE_TOKEN }} + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + CLOUDFLARE_EMAIL: ${{ secrets.CLOUDFLARE_EMAIL }} + GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }} + GITHUB_TOKEN: ${{ github.token }} + PULL_REQUEST: ${{ github.event.pull_request.number }} diff --git a/.husky/post-commit b/.husky/post-commit new file mode 100755 index 0000000..bea6e6c --- /dev/null +++ b/.husky/post-commit @@ -0,0 +1,2 @@ +pnpm exec tsx ./scripts/commit-byterover-context.ts + diff --git a/.husky/pre-commit b/.husky/pre-commit index 5a27ebf..f2efcca 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,3 +1 @@ -pnpm run format:check -pnpm run lint -pnpm run typecheck +pnpm run lint:staged diff --git a/.husky/pre-push b/.husky/pre-push index 347ade0..9936862 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1 +1,2 @@ pnpm verify +pnpm exec tsx ./scripts/commit-byterover-context.ts --check diff --git a/.lintstagedrc.json b/.lintstagedrc.json new file mode 100644 index 0000000..69abf9b --- /dev/null +++ b/.lintstagedrc.json @@ -0,0 +1,3 @@ +{ + "*.{js,jsx,ts,tsx,mjs,cjs,json,jsonc,md,yml,yaml,css,astro}": ["oxfmt --check", "oxlint"] +} diff --git a/README.md b/README.md index 454dbe2..f4455f2 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,8 @@

Caplets

- Capability cards for coding agents.
- Wrap sprawling tool stacks behind focused, progressive-disclosure interfaces. + Give agents capabilities, not tool walls.
+ Turn MCP servers, APIs, and commands into focused agent capabilities.

@@ -15,6 +15,10 @@ Node 22+

+

+ caplets.dev +

+

MCP · OpenAPI · GraphQL · HTTP · CLI

@@ -22,38 +26,20 @@ --- -Caplets turns sprawling tool setups into focused capability cards for coding agents. -Connect MCP servers, OpenAPI specs, GraphQL endpoints, HTTP actions, and curated CLI -commands without flooding the model with every downstream operation up front. +Caplets turns MCP servers, APIs, and commands into focused agent capabilities: one card first, searchable tools next, inspectable schemas before calls, and preserved results after. -Instead of exposing a flat wall of tools, Caplets shows one top-level tool per capability. -The agent chooses a domain first, then uses scoped operations like `search_tools`, -`get_tool`, and `call_tool` only when it needs more detail. +Caplets wraps each tool source as a capability an agent can discover, inspect, call, and recover from one step at a time. Instead of exposing a flat wall of operations, Caplets shows a compact capability card with source, status, and next actions. The agent chooses a domain first, then uses scoped operations like `search_tools`, `get_tool`, and `call_tool` only when it needs more detail. -For MCP-backed Caplets, the scoped operation set also includes resource discovery/reading, -prompt listing/rendering, resource-template discovery, and completion for prompt or template -arguments. Non-MCP backends continue to expose only tool/action operations. +For MCP-backed Caplets, the scoped operation set also includes resource discovery and reading, prompt listing and rendering, resource-template discovery, and completion for prompt or template arguments. Non-MCP backends expose focused tool and action operations. ## Quick Start Caplets requires Node.js 22 or newer. ```sh -pnpm add -g caplets +npm install -g caplets caplets init -``` - -Add a capability from an existing system: - -```sh -# Wrap an MCP server -caplets add mcp docs --command npx --arg -y --arg @upstash/context7-mcp - -# Convert useful repository commands into curated tools -caplets add cli repo-tools --repo . --include git,gh,package - -# Install ready-made Caplets from a repository -caplets install spiritledsoftware/caplets github linear context7 +caplets serve ``` Connect Caplets to any MCP client: @@ -69,10 +55,22 @@ Connect Caplets to any MCP client: } ``` -Ask your agent to use Caplets. It will see a compact capability list first, then inspect -only the backend it needs. +Ask your agent to use Caplets. It will see a compact capability list first, then inspect only the backend it needs. -You can also invoke configured Caplets directly from the CLI for agent-friendly scripts and smoke tests: +Add capabilities from existing systems when you are ready to give agents a focused tool surface: + +```sh +# Wrap an MCP server +caplets add mcp docs --command npx --arg -y --arg @upstash/context7-mcp + +# Convert useful repository commands into curated tools +caplets add cli repo-tools --repo . --include git,gh,package + +# Install ready-made Caplets from a repository +caplets install spiritledsoftware/caplets github linear context7 +``` + +Configured Caplets can be invoked directly from the CLI for agent-friendly scripts and smoke tests: ```sh caplets get-caplet context7 @@ -244,12 +242,16 @@ version, and customize them with the project. ## Why It Matters -Large MCP setups can make agents harder to steer. If every downstream server exposes -every tool up front, the model starts with a noisy flat list, duplicate tool names, and -a larger context surface before it knows which capability matters. +Flat tool lists make agents guess before they understand. If every downstream server exposes every operation up front, the model starts with a noisy list, duplicate tool names, and a larger context surface before it knows which capability matters. -Caplets turns that flat tool wall into progressive disclosure: one capability card first, -then scoped discovery only after the agent chooses the relevant domain. +Caplets turns that flat wall into a staged path: + +1. **Choose** a capability, such as `GitHub`. +2. **Inspect** matching operations with `search_tools` or `list_tools`. +3. **Resolve** the exact schema with `get_tool`. +4. **Invoke** with `call_tool` while preserving downstream content, structured data, and error state. + +A backend enters agent context as a focused card with source, status, and next actions, not a wall of operations. ## Benchmark @@ -290,18 +292,30 @@ CAPLETS_BENCH_LIVE=1 pnpm benchmark:live:opencode -- --model openai/gpt-5.5-fast ## Design Model -Caplets combines two ideas that work well separately but leave a gap together: agent -skills and MCP servers. +Caplets combines two ideas that work well separately but leave a gap together: agent skills and MCP servers. + +Agent skills are great at progressive disclosure. They show an agent a compact capability card first, then let it read deeper instructions only when that skill is relevant. MCP servers are great at live tool execution, but most clients expose their tools as one flat list up front. That means a powerful MCP setup can flood the agent with every tool from every server before it knows which capability area matters. + +Caplets borrows the skill-shaped discovery model and applies it to MCP, OpenAPI, GraphQL, HTTP, and CLI backends. Each backend becomes a skill-like capability card first; its actual operations stay hidden until the agent chooses that capability and asks to search, list, inspect, or call them. + +A capability is safe for agents when it reveals itself in stages: + +- **Discoverable as one capability:** source, status, auth posture, and next actions are visible before any downstream tool list enters context. +- **Inspectable before invocation:** agents search inside the selected capability, then inspect exact tool schemas before any call is made. +- **Lossless after the call:** Caplets preserves structured content, resource links, images, and downstream error state instead of flattening results away. + +## Trust Before Invocation + +Caplets keeps trust mechanics visible before an agent calls a backend. -Agent skills are great at progressive disclosure. They show an agent a compact capability -card first, then let it read deeper instructions only when that skill is relevant. MCP -servers are great at live tool execution, but most clients expose their tools as one flat -list up front. That means a powerful MCP setup can flood the agent with every tool from -every server before it knows which capability area matters. +| Mechanic | Example | Why it matters | +| -------- | ---------------------------------- | ------------------------------------------------------------------------------- | +| Source | `.caplets/config.json` | Users can see where the capability came from before trusting it. | +| Auth | `GITHUB_TOKEN: redacted` | Secrets stay hidden while auth state remains inspectable. | +| Timeout | `30s boundary` | Slow or stuck backends fail visibly instead of disappearing into agent context. | +| Error | `safe message + raw detail scoped` | Recovery information stays useful without leaking sensitive configuration. | -Caplets borrows the skill-shaped discovery model and applies it to MCP. Each downstream -server becomes a skill-like capability card first; its actual MCP tools stay hidden until -the agent chooses that server and asks to search, list, inspect, or call them. +If a backend fails, Caplets keeps the error scoped to the capability, preserves useful recovery detail, and redacts sensitive configuration before it reaches the agent. ## Capabilities diff --git a/alchemy.run.ts b/alchemy.run.ts index 9d767fd..ed65db3 100644 --- a/alchemy.run.ts +++ b/alchemy.run.ts @@ -3,34 +3,37 @@ import { Astro } from "alchemy/cloudflare"; import { GitHubComment } from "alchemy/github"; import { CloudflareStateStore } from "alchemy/state"; -const baseDomain = "caplets.dev"; +const globalBaseDomain = "caplets.dev"; const app = await alchemy("caplets", { stateStore: (scope) => new CloudflareStateStore(scope), password: process.env.ALCHEMY_PASSWORD!, }); -const landingPageDomain = app.stage === "prod" ? baseDomain : `${app.stage}.preview.${baseDomain}`; -const [repositoryOwnerFromSlug, repositoryNameFromSlug] = - process.env.GITHUB_REPOSITORY?.split("/") ?? []; -const repositoryOwner = process.env.GITHUB_REPOSITORY_OWNER ?? repositoryOwnerFromSlug; -const repositoryName = process.env.GITHUB_REPOSITORY_NAME ?? repositoryNameFromSlug; -const pullRequestNumber = process.env.PULL_REQUEST - ? Number.parseInt(process.env.PULL_REQUEST, 10) - : undefined; +const baseDomain = + app.stage === "prod" ? globalBaseDomain : `${app.stage}.preview.${globalBaseDomain}`; +const landingPageDomain = baseDomain; +const landingPageUrl = `https://${landingPageDomain}`; export const landingPage = await Astro("landing-page", { cwd: "apps/landing", dev: { command: "pnpm run dev" + (process.env.SSH_CONNECTION ? " --host 0.0.0.0" : ""), }, - domains: [landingPageDomain], + domains: [landingPageDomain, `www.${landingPageDomain}`], }); console.log({ - "Landing Page URL": landingPage.url, + "Landing Page URL": landingPageUrl, }); +const [repositoryOwnerFromSlug, repositoryNameFromSlug] = + process.env.GITHUB_REPOSITORY?.split("/") ?? []; +const repositoryOwner = process.env.GITHUB_REPOSITORY_OWNER ?? repositoryOwnerFromSlug; +const repositoryName = process.env.GITHUB_REPOSITORY_NAME ?? repositoryNameFromSlug; +const pullRequestNumber = process.env.PULL_REQUEST + ? Number.parseInt(process.env.PULL_REQUEST, 10) + : undefined; if (pullRequestNumber) { if (!repositoryOwner || !repositoryName) { throw new Error("Missing GitHub repository metadata for preview comment."); @@ -44,7 +47,7 @@ if (pullRequestNumber) { Your changes have been deployed to a preview environment: -**🌐 Landing Page:** ${landingPage.url} +**🌐 Landing Page:** ${landingPageUrl} Built from commit ${process.env.GITHUB_SHA?.slice(0, 7) ?? "unknown"} diff --git a/apps/landing/src/pages/index.astro b/apps/landing/src/pages/index.astro index fa30677..b5f8c7f 100644 --- a/apps/landing/src/pages/index.astro +++ b/apps/landing/src/pages/index.astro @@ -9,7 +9,7 @@ const heroTrace = { steps: [ { label: "get_caplet", - detail: "Expose one card before any downstream tool list enters context.", + detail: "Show one capability card before any downstream tool list enters context.", result: "search_tools · get_tool · call_tool", }, { @@ -19,29 +19,29 @@ const heroTrace = { }, { label: 'get_tool("create_pull_request")', - detail: "Inspect the preserved schema before an agent can invoke the operation.", + detail: "Inspect the exact schema before an agent can invoke the operation.", result: "title · body · base · head · reviewers?", }, { label: "call_tool(arguments)", - detail: "Forward the call and keep downstream content, structured data, and errors intact.", + detail: "Forward the call and keep content, structured data, and errors intact.", result: "structuredContent + content", }, ], }; -const skillifyFramework = [ +const capabilityFramework = [ { - title: "Discoverable as one capability", - copy: "A backend enters the agent context as a focused card with source, status, and next actions, not a flat wall of operations.", + title: "One capability first", + copy: "Each tool source enters agent context as a focused card with source, status, and next actions, not a long list of operations.", }, { - title: "Inspectable before invocation", - copy: "Agents search inside the selected capability, then inspect exact tool schemas before any call is made.", + title: "Schemas before calls", + copy: "Agents search inside one capability, then inspect the exact tool schema before they invoke anything.", }, { - title: "Lossless after the call", - copy: "Caplets preserves structured content, resource links, images, and downstream error state instead of flattening results away.", + title: "Results stay intact", + copy: "Caplets preserves structured content, resource links, images, and downstream errors instead of flattening results away.", }, ]; @@ -59,7 +59,7 @@ const trustMechanics = [ { label: "Timeout", value: "30s boundary", - copy: "Slow or stuck backends fail visibly instead of disappearing into agent context.", + copy: "Slow or stuck tools fail visibly instead of disappearing into agent context." }, { label: "Error", @@ -68,7 +68,7 @@ const trustMechanics = [ }, ]; -const backends = ["MCP", "OpenAPI", "GraphQL", "HTTP", "CLI"]; +const sources = ["MCP", "OpenAPI", "GraphQL", "HTTP", "CLI"]; const agentSetups = [ { id: "claude-code", @@ -183,14 +183,14 @@ const installSteps = [

Capability cards for coding agents

-

Skillify your backends.

+

Give agents capabilities, not tool walls.

Caplets turns MCP servers, APIs, and commands into focused agent capabilities: one card first, searchable tools next, inspectable schemas before calls, and preserved results after.

- To skillify a backend is to wrap it as a capability an agent can discover, inspect, call, - and recover from one step at a time. + Wrap each MCP server, API, or command set as a capability an agent can discover, inspect, + call, and recover from one step at a time.

Install Caplets @@ -198,8 +198,8 @@ const installSteps = [
-
Backends
-
{backends.join(" · ")}
+
Sources
+
{sources.join(" · ")}
Clients
@@ -239,20 +239,20 @@ const installSteps = [
-

The wall of tools problem

-

Flat tool lists make agents guess before they understand.

+

The tool wall problem

+

Flat tool lists force agents to choose before they understand.

Before

-

Every downstream operation arrives at once.

+

Every operation enters context before the agent knows which one matters.

After

-

Agents inspect a capability path one step at a time.

+

Agents choose a capability, then inspect only the operations inside it.

  1. Choosegithub
  2. Inspectsearch_tools
  3. @@ -265,11 +265,11 @@ const installSteps = [
    -

    What skillify means

    -

    A backend becomes safe for agents when it reveals itself in stages.

    +

    Capability-shaped tools

    +

    A tool source is safer for agents when it reveals itself in stages.

    - {skillifyFramework.map((point, index) => ( + {capabilityFramework.map((point, index) => (

    0{index + 1}

    {point.title}

    @@ -296,7 +296,7 @@ const installSteps = [
    Safe recovery example

    - If a backend fails, Caplets keeps the error scoped to the capability, preserves useful + If a tool fails, Caplets keeps the error scoped to that capability, preserves useful recovery detail, and redacts sensitive configuration before it reaches the agent.

    @@ -304,8 +304,8 @@ const installSteps = [
    -

    Native where it helps

    -

    Use Caplets from the agent you already run.

    +

    Works where agents work

    +

    Run Caplets from the coding agent you already use.

    @@ -368,8 +368,8 @@ const installSteps = [
    -

    Start with one capability

    -

    Install, add a backend, serve capabilities.

    +

    Start with one tool source

    +

    Install Caplets, add a source, serve capabilities.

    Caplets can run as a universal MCP server, a native Pi or OpenCode tool layer, or a remote HTTP service for shared environments. @@ -400,7 +400,7 @@ const installSteps = [

    diff --git a/package.json b/package.json index 43ee679..fe386db 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "format:check": "oxfmt --check .", "lint": "oxlint .", "lint:fix": "oxlint --fix .", + "lint:staged": "lint-staged", "prepare": "husky", "release": "turbo build && changeset publish", "schema:check": "tsx ./scripts/generate-config-schema.ts --check", @@ -36,6 +37,7 @@ "@typescript/native-preview": "7.0.0-dev.20260527.1", "alchemy": "0.93.9", "husky": "^9.1.7", + "lint-staged": "^17.0.5", "oxfmt": "^0.52.0", "oxlint": "^1.67.0", "rolldown": "^1.0.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c1932c..f6faf2b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -26,6 +26,9 @@ importers: husky: specifier: ^9.1.7 version: 9.1.7 + lint-staged: + specifier: ^17.0.5 + version: 17.0.5 oxfmt: specifier: ^0.52.0 version: 0.52.0 @@ -2775,6 +2778,10 @@ packages: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} + ansi-escapes@7.3.0: + resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} + engines: {node: '>=18'} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -2936,6 +2943,14 @@ packages: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@5.2.0: + resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} + engines: {node: '>=20'} + cliui@8.0.1: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} @@ -3221,6 +3236,9 @@ packages: emmet@2.4.11: resolution: {integrity: sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ==} + emoji-regex@10.6.0: + resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} + emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3251,6 +3269,10 @@ packages: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} @@ -3696,6 +3718,10 @@ packages: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} + is-fullwidth-code-point@5.1.0: + resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} + engines: {node: '>=18'} + is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -3881,6 +3907,15 @@ packages: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lint-staged@17.0.5: + resolution: {integrity: sha512-d12yC+/e8RhBjZtaxZn71FyrgU/P5e+uAPifhCLwdosQZP/zamSdKRWDC30ocVIbzDKiFG1McHc/LUgB92GIPw==} + engines: {node: '>=22.22.1'} + hasBin: true + + listr2@10.2.1: + resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==} + engines: {node: '>=22.13.0'} + locate-path@5.0.0: resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} engines: {node: '>=8'} @@ -3888,6 +3923,10 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + loglevel@1.9.2: resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} engines: {node: '>= 0.6.0'} @@ -4076,6 +4115,10 @@ packages: resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} engines: {node: '>=18'} + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + miniflare@4.20260424.0: resolution: {integrity: sha512-B6MKBBd5TJ19daUc3Ae9rWctn1nDA/VCXykXfCsp9fTxyfGxnZY27tJs1caxgE9MWEMMKGbGHouqVtgKbKGxmw==} engines: {node: '>=18.0.0'} @@ -4196,6 +4239,10 @@ packages: once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + oniguruma-parser@0.12.2: resolution: {integrity: sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==} @@ -4513,6 +4560,10 @@ packages: resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + retext-latin@4.0.0: resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} @@ -4537,6 +4588,9 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + rolldown@1.0.3: resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} @@ -4659,6 +4713,14 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} + slice-ansi@7.1.2: + resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} + engines: {node: '>=18'} + + slice-ansi@8.0.0: + resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} + engines: {node: '>=20'} + smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} @@ -4686,6 +4748,10 @@ packages: std-env@4.1.0: resolution: {integrity: sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==} + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -4694,6 +4760,14 @@ packages: resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} engines: {node: '>=12'} + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string-width@8.2.1: + resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} + engines: {node: '>=20'} + string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} @@ -5201,6 +5275,10 @@ packages: '@cloudflare/workers-types': optional: true + wrap-ansi@10.0.0: + resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} + engines: {node: '>=20'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -5209,6 +5287,10 @@ packages: resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} engines: {node: '>=12'} + wrap-ansi@9.0.2: + resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} + engines: {node: '>=18'} + wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} @@ -7621,6 +7703,10 @@ snapshots: ansi-colors@4.1.3: {} + ansi-escapes@7.3.0: + dependencies: + environment: 1.1.0 + ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} @@ -7844,6 +7930,15 @@ snapshots: ci-info@4.4.0: {} + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-truncate@5.2.0: + dependencies: + slice-ansi: 8.0.0 + string-width: 8.2.1 + cliui@8.0.1: dependencies: string-width: 4.2.3 @@ -8022,6 +8117,8 @@ snapshots: '@emmetio/abbreviation': 2.3.3 '@emmetio/css-abbreviation': 2.1.8 + emoji-regex@10.6.0: {} + emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} @@ -8044,6 +8141,8 @@ snapshots: env-paths@3.0.0: {} + environment@1.1.0: {} + error-stack-parser-es@1.0.5: {} es-define-property@1.0.1: {} @@ -8671,6 +8770,10 @@ snapshots: is-fullwidth-code-point@3.0.0: {} + is-fullwidth-code-point@5.1.0: + dependencies: + get-east-asian-width: 1.6.0 + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 @@ -8824,12 +8927,37 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lint-staged@17.0.5: + dependencies: + listr2: 10.2.1 + picomatch: 4.0.4 + string-argv: 0.3.2 + tinyexec: 1.2.2 + optionalDependencies: + yaml: 2.9.0 + + listr2@10.2.1: + dependencies: + cli-truncate: 5.2.0 + eventemitter3: 5.0.4 + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 10.0.0 + locate-path@5.0.0: dependencies: p-locate: 4.1.0 lodash.startcase@4.4.0: {} + log-update@6.1.0: + dependencies: + ansi-escapes: 7.3.0 + cli-cursor: 5.0.0 + slice-ansi: 7.1.2 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + loglevel@1.9.2: {} long@5.3.2: {} @@ -9188,6 +9316,8 @@ snapshots: dependencies: mime-db: 1.54.0 + mimic-function@5.0.1: {} + miniflare@4.20260424.0: dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -9312,6 +9442,10 @@ snapshots: dependencies: wrappy: 1.0.2 + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + oniguruma-parser@0.12.2: {} oniguruma-to-es@4.3.6: @@ -9659,6 +9793,11 @@ snapshots: resolve-pkg-maps@1.0.0: {} + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + retext-latin@4.0.0: dependencies: '@types/nlcst': 2.0.3 @@ -9690,6 +9829,8 @@ snapshots: reusify@1.1.0: {} + rfdc@1.4.1: {} + rolldown@1.0.3: dependencies: '@oxc-project/types': 0.133.0 @@ -9898,6 +10039,16 @@ snapshots: slash@3.0.0: {} + slice-ansi@7.1.2: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + + slice-ansi@8.0.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.1.0 + smol-toml@1.6.1: {} source-map-js@1.2.1: {} @@ -9917,6 +10068,8 @@ snapshots: std-env@4.1.0: {} + string-argv@0.3.2: {} + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -9929,6 +10082,17 @@ snapshots: emoji-regex: 9.2.2 strip-ansi: 7.2.0 + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + + string-width@8.2.1: + dependencies: + get-east-asian-width: 1.6.0 + strip-ansi: 7.2.0 + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 @@ -10360,6 +10524,12 @@ snapshots: - bufferutil - utf-8-validate + wrap-ansi@10.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 8.2.1 + strip-ansi: 7.2.0 + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -10372,6 +10542,12 @@ snapshots: string-width: 5.1.2 strip-ansi: 7.2.0 + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrappy@1.0.2: {} ws@8.18.0: {} diff --git a/scripts/commit-byterover-context.test.ts b/scripts/commit-byterover-context.test.ts new file mode 100644 index 0000000..a707533 --- /dev/null +++ b/scripts/commit-byterover-context.test.ts @@ -0,0 +1,26 @@ +import { expect, test } from "vitest"; + +import { + buildCommitArgs, + checkByteRoverStatus, + formatCheckWarning, +} from "./commit-byterover-context"; + +test("ByteRover status check reports clean when git returns no .brv changes", () => { + expect(checkByteRoverStatus("")).toEqual({ hasChanges: false }); +}); + +test("ByteRover status check reports changes from porcelain output", () => { + expect(checkByteRoverStatus(" M .brv/context.md\n?? .brv/new.md\n")).toEqual({ + hasChanges: true, + }); +}); + +test("ByteRover check warning is advisory and includes manual commit command", () => { + expect(formatCheckWarning()).toContain("ByteRover context has uncommitted changes"); + expect(formatCheckWarning()).toContain("pnpm exec tsx ./scripts/commit-byterover-context.ts"); +}); + +test("ByteRover context commits use a conventional docs commit message", () => { + expect(buildCommitArgs()).toEqual(["commit", "-m", "docs(agents): byterover context"]); +}); diff --git a/scripts/commit-byterover-context.ts b/scripts/commit-byterover-context.ts new file mode 100644 index 0000000..25fa2f9 --- /dev/null +++ b/scripts/commit-byterover-context.ts @@ -0,0 +1,84 @@ +import { spawnSync } from "node:child_process"; +import { fileURLToPath } from "node:url"; + +const recursionGuardEnv = "BYTEROVER_CONTEXT_COMMIT"; +const commitMessage = "docs(agents): byterover context"; + +export type ByteRoverStatus = { + hasChanges: boolean; +}; + +export function checkByteRoverStatus(porcelainOutput: string): ByteRoverStatus { + return { hasChanges: porcelainOutput.trim().length > 0 }; +} + +export function buildCommitArgs(): string[] { + return ["commit", "-m", commitMessage]; +} + +export function formatCheckWarning(): string { + return [ + "ByteRover context has uncommitted changes.", + "These changes are advisory and will not block this push.", + "Run `pnpm exec tsx ./scripts/commit-byterover-context.ts` to commit them.", + ].join("\n"); +} + +function runGit(args: string[], env: NodeJS.ProcessEnv = process.env): string { + const result = spawnSync("git", args, { + encoding: "utf8", + env, + stdio: ["ignore", "pipe", "pipe"], + }); + + if (result.status !== 0) { + const detail = result.stderr.trim() || result.stdout.trim(); + throw new Error(`git ${args.join(" ")} failed${detail ? `: ${detail}` : ""}`); + } + + return result.stdout; +} + +function isCheckMode(args: string[]): boolean { + return args.includes("--check"); +} + +export function main(args = process.argv.slice(2)): number { + if (process.env[recursionGuardEnv] === "1") { + return 0; + } + + const status = checkByteRoverStatus(runGit(["status", "--porcelain", "--", ".brv"])); + + if (!status.hasChanges) { + return 0; + } + + if (isCheckMode(args)) { + console.warn(formatCheckWarning()); + return 0; + } + + runGit(["add", "--", ".brv"]); + const stagedStatus = checkByteRoverStatus( + runGit(["diff", "--cached", "--name-status", "--", ".brv"]), + ); + + if (!stagedStatus.hasChanges) { + return 0; + } + + runGit(buildCommitArgs(), { ...process.env, [recursionGuardEnv]: "1" }); + return 0; +} + +const currentFilePath = fileURLToPath(import.meta.url); + +if (process.argv[1] === currentFilePath) { + try { + process.exitCode = main(); + } catch (error) { + console.error(error instanceof Error ? error.message : String(error)); + process.exitCode = 1; + } +}