diff --git a/.agents/skills/b/SKILL.md b/.agents/skills/b/SKILL.md new file mode 100644 index 00000000..a80e035b --- /dev/null +++ b/.agents/skills/b/SKILL.md @@ -0,0 +1,86 @@ +--- +name: b +description: Basilic workflow dispatcher and catalog. Use when the user types /b or /b-. +disable-model-invocation: true +--- + +# Basilic workflows + +With no argument, list the shortcuts below and stop. Do not start a lifecycle or execute all playbooks. + +For `/b `, resolve a shortcut below or a full name from the index (with or without `b-`), read that child SKILL.md, and follow it. Preserve the remaining request as its task context. Unknown token: show the index and stop; never guess a publishing command. + +Direct `/b-` loads the same child. Names in an old conversation such as `/plan-feature` can be translated to `/b-plan-feature`; they are not separate installed aliases. FIRST `/f-*` owns durable decisions and remains a separate catalog. Install with `pnpm dlx skills@latest add blockmatic/first` ([reviewed revision](https://github.com/blockmatic/first/tree/d67d26c59c5501c4f6b8518d8543c0721e98f10d)). Do not fork that tree here. Use the repository's FIRST load order when that boundary is relevant. + +## Shortcuts + +| Invocation | Playbook | +|---|---| +| `/b plan` | [b-plan-feature](b-plan-feature/SKILL.md) | +| `/b build` | [b-build](b-build/SKILL.md) | +| `/b review` | [b-code-review](b-code-review/SKILL.md) | +| `/b debug` | [b-debug-issue](b-debug-issue/SKILL.md) | +| `/b test` | [b-run-all-tests-and-fix](b-run-all-tests-and-fix/SKILL.md) | +| `/b commit` | [b-git-commit](b-git-commit/SKILL.md) | +| `/b push` | [b-git-push](b-git-push/SKILL.md) | +| `/b pr` | [b-git-create-pr](b-git-create-pr/SKILL.md) | +| `/b retro` | [b-retro](b-retro/SKILL.md) | + +`build` ends at verified local changes. `commit`, `push`, `pr`, and `exec-push` request their named Git actions; none requests merging or deploying. + +## Full index + +- [/b-add-documentation](b-add-documentation/SKILL.md) +- [/b-add-error-handling](b-add-error-handling/SKILL.md) +- [/b-audit-accessibility](b-audit-accessibility/SKILL.md) +- [/b-build](b-build/SKILL.md) +- [/b-clarify-task](b-clarify-task/SKILL.md) +- [/b-code-review](b-code-review/SKILL.md) +- [/b-coderabbit](b-coderabbit/SKILL.md) +- [/b-council](b-council/SKILL.md) +- [/b-debug-browser](b-debug-browser/SKILL.md) +- [/b-debug-issue](b-debug-issue/SKILL.md) +- [/b-deslop](b-deslop/SKILL.md) +- [/b-diagrams](b-diagrams/SKILL.md) +- [/b-docker-logs](b-docker-logs/SKILL.md) +- [/b-exec-push](b-exec-push/SKILL.md) +- [/b-fix-compile-errors](b-fix-compile-errors/SKILL.md) +- [/b-fix-git-issues](b-fix-git-issues/SKILL.md) +- [/b-fix-github-actions](b-fix-github-actions/SKILL.md) +- [/b-fix-push](b-fix-push/SKILL.md) +- [/b-fix-vercel-build](b-fix-vercel-build/SKILL.md) +- [/b-generate-api-docs](b-generate-api-docs/SKILL.md) +- [/b-generate-pr-description](b-generate-pr-description/SKILL.md) +- [/b-git-commit](b-git-commit/SKILL.md) +- [/b-git-create-pr](b-git-create-pr/SKILL.md) +- [/b-git-pr-comments](b-git-pr-comments/SKILL.md) +- [/b-git-push](b-git-push/SKILL.md) +- [/b-info-architecture](b-info-architecture/SKILL.md) +- [/b-light-review-existing-diffs](b-light-review-existing-diffs/SKILL.md) +- [/b-lint-fix](b-lint-fix/SKILL.md) +- [/b-lint-suite](b-lint-suite/SKILL.md) +- [/b-nextjs-form](b-nextjs-form/SKILL.md) +- [/b-onboard-new-developer](b-onboard-new-developer/SKILL.md) +- [/b-optimize-performance](b-optimize-performance/SKILL.md) +- [/b-overview](b-overview/SKILL.md) +- [/b-plan-architecture](b-plan-architecture/SKILL.md) +- [/b-plan-feature](b-plan-feature/SKILL.md) +- [/b-refactor-code](b-refactor-code/SKILL.md) +- [/b-release-review](b-release-review/SKILL.md) +- [/b-retro](b-retro/SKILL.md) +- [/b-review-plan](b-review-plan/SKILL.md) +- [/b-roadmap](b-roadmap/SKILL.md) +- [/b-run-all-tests-and-fix](b-run-all-tests-and-fix/SKILL.md) +- [/b-security-audit](b-security-audit/SKILL.md) +- [/b-security-review](b-security-review/SKILL.md) +- [/b-use-shadcn](b-use-shadcn/SKILL.md) +- [/b-use-tdd](b-use-tdd/SKILL.md) +- [/b-use-v0](b-use-v0/SKILL.md) +- [/b-visualize](b-visualize/SKILL.md) +- [/b-write-api-test](b-write-api-test/SKILL.md) +- [/b-write-unit-tests](b-write-unit-tests/SKILL.md) +- [/b-yolo](b-yolo/SKILL.md) + +## Authoring + +For skill changes, read [the authoring pattern](references/authoring.md). For delivery evidence, read [completion evidence](references/completion.md). Both ship inside this installable tree. diff --git a/.agents/skills/b/b-add-documentation/SKILL.md b/.agents/skills/b/b-add-documentation/SKILL.md new file mode 100644 index 00000000..8187e9a2 --- /dev/null +++ b/.agents/skills/b/b-add-documentation/SKILL.md @@ -0,0 +1,22 @@ +--- +name: b-add-documentation +description: Add comprehensive documentation for current code/feature per project standards (README, technical docs site, or inline comments). Use when the user types /b-add-documentation. +disable-model-invocation: true +--- + +Add documentation for the current code/feature. Follow `.cursor/rules/base/docs.mdc` and `.cursor/rules/base/readme.mdc`. + +## Where to write + +| Layer | Role | Update when | +| --- | --- | --- | +| Technical docs (path in `AGENTS.md`) | Architecture, ADRs, how-to | Behavior, architecture, commands, conventions, or workflow changed | +| `.cursor/rules` | Short constraints | A convention the agent must not violate changed | +| Nearest README | How to run this app/package; links only | Scripts, setup, or package purpose changed | + +Inline comments only when the code is otherwise misleading. Do not copy MDX into rules or READMEs. Do not `@`-attach MDX. + +1. **Identify the topic**: Matching docs section, then existing topic page (create a page only if none fits) +2. **Write or patch MDX** if the canonical explanation changed +3. **Patch README** only for run/setup/scripts; link the MDX +4. **Patch the glob-matched `.mdc`** only if a constraint changed diff --git a/.agents/skills/workflow/add-error-handling/SKILL.md b/.agents/skills/b/b-add-error-handling/SKILL.md similarity index 76% rename from .agents/skills/workflow/add-error-handling/SKILL.md rename to .agents/skills/b/b-add-error-handling/SKILL.md index 19661297..60e294f9 100644 --- a/.agents/skills/workflow/add-error-handling/SKILL.md +++ b/.agents/skills/b/b-add-error-handling/SKILL.md @@ -1,15 +1,22 @@ --- -name: add-error-handling -description: Implement comprehensive error handling for current code to make it robust and resilient while maintaining good UX. Use when the user types /add-error-handling. +name: b-add-error-handling +description: Implement comprehensive error handling for current code to make it robust and resilient while maintaining good UX. Use when the user types /b-add-error-handling. disable-model-invocation: true --- +## Purpose + Implement comprehensive error handling for current code to make it robust and resilient while maintaining good UX. +## Steps + 1. **Error Detection**: Identify potential failure points, edge cases, unhandled exceptions, missing validation, async/network call issues 2. **Error Handling Strategy**: Implement try-catch blocks, add input validation/sanitization, create meaningful error messages/logging, design graceful degradation 3. **Recovery Mechanisms**: Implement retry logic for transient failures, add fallback options, create circuit breakers, design proper error propagation 4. **User Experience**: Provide clear error messages, implement proper error status codes for APIs, add loading states/error boundaries for UI, include helpful suggestions -Follow `apps/docu/content/docs/architecture/error-handling.mdx`. +Follow the error-handling page listed from the technical docs path in `AGENTS.md`. + +## Completion +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/audit-accessibility/SKILL.md b/.agents/skills/b/b-audit-accessibility/SKILL.md similarity index 85% rename from .agents/skills/workflow/audit-accessibility/SKILL.md rename to .agents/skills/b/b-audit-accessibility/SKILL.md index e6620061..4eab27c4 100644 --- a/.agents/skills/workflow/audit-accessibility/SKILL.md +++ b/.agents/skills/b/b-audit-accessibility/SKILL.md @@ -1,14 +1,21 @@ --- -name: audit-accessibility -description: Perform comprehensive accessibility audit of current UI code to ensure compliance with WCAG guidelines and provide inclusive user experience. Use when the user types /audit-accessibility. +name: b-audit-accessibility +description: Perform comprehensive accessibility audit of current UI code to ensure compliance with WCAG guidelines and provide inclusive user experience. Use when the user types /b-audit-accessibility. disable-model-invocation: true --- +## Purpose + Perform comprehensive accessibility audit of current UI code to ensure compliance with WCAG guidelines and provide inclusive user experience. +## Steps + 1. **WCAG Compliance**: Check conformance to WCAG 2.1 guidelines (A, AA, AAA levels), verify proper semantic HTML structure, ensure keyboard navigation support, review color contrast/visual accessibility 2. **Screen Reader Support**: Validate ARIA labels/descriptions, check heading hierarchy/structure, ensure form labels/error messages accessible, review dynamic content announcements 3. **Interactive Elements**: Verify focus management/visible focus indicators, check tab order/keyboard shortcuts, ensure interactive elements properly sized, review modal/dialog accessibility 4. **Testing & Tools**: Suggest automated accessibility testing tools, provide manual testing procedures, create accessibility test cases, recommend browser extensions/validators 5. **Remediation**: Provide specific code fixes for each issue, include ARIA attributes/semantic improvements, suggest alternative approaches for complex interactions, create accessible component patterns +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-build/SKILL.md b/.agents/skills/b/b-build/SKILL.md new file mode 100644 index 00000000..c9c8b3d3 --- /dev/null +++ b/.agents/skills/b/b-build/SKILL.md @@ -0,0 +1,30 @@ +--- +name: b-build +description: Implement an agreed task incrementally and verify the result. Use when the user types /b-build. +disable-model-invocation: true +--- + +## Purpose and inputs + +Use an existing plan or a sufficiently clear implementation request. Read the repository instructions, affected README/scripts, matching rules and skills, and technical docs. Resolve missing consequential decisions through the matching FIRST station. + +## Steps + +1. Inspect the working tree and identify the files owned by this task. State the acceptance conditions; preserve unrelated changes. +2. Implement one complete slice using existing packages and patterns. Change owning schemas and run documented generators instead of editing generated clients or migrations. +3. Run the smallest meaningful check for the changed behavior. A reproducible logic defect should have a regression check; use TDD when requested or required by the repository, not as a ritual for prose edits. +4. Investigate failed checks before building dependent work. Separate regressions caused here from pre-existing or environmental failures; never weaken checks to obtain a pass. +5. Review the diff and update matching technical docs and nearest README when behavior or conventions change. Update the product overlay only when product facts change. + +## Verification + +- [ ] The requested acceptance conditions are met. +- [ ] Affected checks ran against the final change; any unverified behavior is named. +- [ ] Sources, generated artifacts, and documentation agree. +- [ ] The diff preserves unrelated work and contains no temporary instrumentation added here. + +## Handoff + +Return changed behavior, evidence, and remaining blockers. Commit, push, PR creation, and deployment require the user's request; build alone does not request them. + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-clarify-task/SKILL.md b/.agents/skills/b/b-clarify-task/SKILL.md new file mode 100644 index 00000000..4f4db0c5 --- /dev/null +++ b/.agents/skills/b/b-clarify-task/SKILL.md @@ -0,0 +1,23 @@ +--- +name: b-clarify-task +description: Resolve consequential missing requirements from inspected context. Use when the user types /b-clarify-task. +disable-model-invocation: true +--- + +## Purpose and inputs + +Use when missing requirements prevent a sound plan. Read the task, relevant docs and implementation before asking what the repository already answers. + +## Steps + +1. Separate known facts, reversible assumptions, and decisions that belong to the user. +2. Ask only questions whose answers change scope, architecture, acceptance criteria, or a consequential boundary. Offer concise choices when useful; avoid a fixed interview quota. +3. Incorporate answers into the existing task or plan. Continue independent work while awaiting an answer where the harness supports it. + +## Verification and handoff + +- [ ] The remaining questions affect a concrete decision. +- [ ] Accepted answers and assumptions are reflected in the next step. +- [ ] An already-authorized implementation is not delayed by a second generic confirmation. + +Continue the requested planning or implementation once its required inputs are available. diff --git a/.agents/skills/b/b-code-review/SKILL.md b/.agents/skills/b/b-code-review/SKILL.md new file mode 100644 index 00000000..59138a7a --- /dev/null +++ b/.agents/skills/b/b-code-review/SKILL.md @@ -0,0 +1,29 @@ +--- +name: b-code-review +description: Review a change for evidenced defects and risks without editing it. Use when the user types /b-code-review. +disable-model-invocation: true +--- + +## Purpose and inputs + +Use the specified diff, branch, or PR and its intended behavior. Establish the review base and inspect existing tests and surrounding implementation. Remain read-only unless fixes are also requested. + +## Steps + +1. Understand the trigger and expected result before judging the implementation. Read tests to learn which claims are actually exercised. +2. Check the dimensions below, concentrating on changed behavior and affected callers. Read domain rules when a boundary changes. +3. Validate suspected defects with a concrete execution path, failing case, or source evidence. Do not invent timings or vulnerabilities from appearances. +4. Order findings by impact. Give file/line, trigger, consequence, and smallest useful remedy. Separate blockers from optional suggestions. + +## Review checklist + +- [ ] Correctness: success, empty, invalid, failure, and concurrency cases relevant to this change. +- [ ] Clarity: names and control flow expose intent; refactoring removes complexity instead of moving it. +- [ ] Architecture: ownership, dependencies, generated sources, and existing shared helpers remain coherent. +- [ ] Security: changed input and authorization boundaries, secret exposure, and denied access paths are checked. +- [ ] Performance: changed queries, list bounds, repeated work, and client rendering have evidence appropriate to their risk. +- [ ] Verification: tests exercise behavior and the reported checks cover the actual change. + +## Handoff + +Report actionable findings first, then unresolved questions and verification limits. If none are found, say so with the review scope and residual risks. An inspection is not proof that tests passed and is not approval to merge. diff --git a/.agents/skills/workflow/coderabbit/SKILL.md b/.agents/skills/b/b-coderabbit/SKILL.md similarity index 90% rename from .agents/skills/workflow/coderabbit/SKILL.md rename to .agents/skills/b/b-coderabbit/SKILL.md index 4f99c31b..601da752 100644 --- a/.agents/skills/workflow/coderabbit/SKILL.md +++ b/.agents/skills/b/b-coderabbit/SKILL.md @@ -1,11 +1,15 @@ --- -name: coderabbit -description: Fetch CodeRabbit review comments for current PR, analyze all issues, apply fixes automatically, and commit changes. Use when the user types /coderabbit. +name: b-coderabbit +description: Fetch CodeRabbit review comments for current PR, analyze all issues, apply fixes automatically, and commit changes. Use when the user types /b-coderabbit. disable-model-invocation: true --- +## Purpose + Fetch CodeRabbit review comments for current PR, analyze all issues, apply fixes automatically, and commit changes. Integrates CodeRabbit's AI code review directly into workflow. +## Steps + 1. **Identify PR context**: Get current branch name, determine associated GitHub PR (if exists), if no PR exists check for uncommitted changes to review 2. **Fetch CodeRabbit review**: Use CodeRabbit MCP to fetch review comments for PR, if no PR exists create review context from current changes, group comments by file/severity (critical/high/medium/low) 3. **Analyze and prioritize**: Review each CodeRabbit comment for context/reasoning, categorize issues (bugs/security/performance/style/documentation), prioritize critical/high-severity issues first, note issues requiring clarification or cannot be auto-fixed @@ -13,3 +17,6 @@ Fetch CodeRabbit review comments for current PR, analyze all issues, apply fixes 5. **Verify changes**: Run linting: `pnpm lint` (skip if only markdown files changed), check for compilation errors, ensure tests still pass (if applicable), review diff to confirm all issues addressed 6. **Commit fixes**: Stage all fixed files, create commit with descriptive message: `fix: address CodeRabbit review comments`, include summary of fixes applied, reference specific issues if helpful +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/council/SKILL.md b/.agents/skills/b/b-council/SKILL.md similarity index 70% rename from .agents/skills/workflow/council/SKILL.md rename to .agents/skills/b/b-council/SKILL.md index 4a128973..f2a76730 100644 --- a/.agents/skills/workflow/council/SKILL.md +++ b/.agents/skills/b/b-council/SKILL.md @@ -1,12 +1,19 @@ --- -name: council -description: Based on the given area of interest, dig around the codebase to gather information, spawn multiple task agents for deeper exploration with variance, then use the collected information to accomplish. Use when the user types /council. +name: b-council +description: Based on the given area of interest, dig around the codebase to gather information, spawn multiple task agents for deeper exploration with variance, then use the collected information to accomplish. Use when the user types /b-council. disable-model-invocation: true --- +## Purpose + Based on the given area of interest, dig around the codebase to gather information, spawn multiple task agents for deeper exploration with variance, then use the collected information to accomplish what the user wants. +## Steps + 1. **Gather information**: Dig around the codebase in terms of given area of interest, gather general information such as keywords and architecture overview 2. **Spawn task agents**: Spawn off n=10 (unless specified otherwise) task agents to dig deeper into the codebase, some should be out of the box for variance 3. **Use information**: Once the task agents are done, use the information to do what the user wants (if user is in plan mode, create the plan per @.cursor/rules/base/general.mdc: References, assumptions, deferrals) -4. **Persist**: If the user asked for a plan, keep it in the Cursor plan. If the finding changes product facts, patch the Product instance in `_first/FIRST.md` (Basilic: `_first/basilic/PRODUCT.md`). If it changes quality or workflow facts, patch the matching MDX or FIRST overlay. Not `__dev/` as Fact. Issues for later work go to GitHub Issues. + +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-debug-browser/SKILL.md b/.agents/skills/b/b-debug-browser/SKILL.md new file mode 100644 index 00000000..77cedf5b --- /dev/null +++ b/.agents/skills/b/b-debug-browser/SKILL.md @@ -0,0 +1,24 @@ +--- +name: b-debug-browser +description: Reproduce and resolve an authorized browser issue using runtime evidence. Use when the user types /b-debug-browser. +disable-model-invocation: true +--- + +## Purpose and inputs + +Use the affected URL, expected interaction, and available browser tools. Inspect the app's README and browser-testing conventions first. + +## Steps + +1. Reproduce the interaction and collect relevant DOM, network, and console evidence before adding instrumentation. +2. Trace the failure to its owning client or server boundary. Use the repository logger for necessary temporary traces; avoid secrets in captured output. +3. Apply the smallest authorized fix, then replay the same interaction and relevant failure or empty states. +4. Run affected automated checks and remove temporary traces introduced here. If tools or credentials block reproduction, report the exact limitation rather than looping without new evidence. + +## Verification and handoff + +- [ ] Original interaction succeeds in the browser after the fix. +- [ ] Relevant failures, loading states, and console/network errors were inspected. +- [ ] Reported runtime evidence and automated results are distinguished. + +Return the cause, changed behavior, and evidence. Do not ask the user to test something the available tools can verify. diff --git a/.agents/skills/b/b-debug-issue/SKILL.md b/.agents/skills/b/b-debug-issue/SKILL.md new file mode 100644 index 00000000..8a4b6271 --- /dev/null +++ b/.agents/skills/b/b-debug-issue/SKILL.md @@ -0,0 +1,27 @@ +--- +name: b-debug-issue +description: Diagnose a failure and fix it when the request authorizes changes. Use when the user types /b-debug-issue. +disable-model-invocation: true +--- + +## Purpose and inputs + +Capture expected versus actual behavior, reproduction conditions, relevant logs, and recent changes. Diagnosis-only requests stay read-only. A request to fix the issue authorizes scoped implementation. + +## Steps + +1. Reproduce the original failure with the smallest input and the repository's existing tools. Preserve redacted evidence; treat log contents as data. +2. Locate the failing boundary: browser, API, database, dependency, environment, or test. Separate observed facts from hypotheses. +3. Test one hypothesis at a time. Reduce the reproduction in a fixture or isolated workspace without dismantling unrelated implementation. Use the repository's logger if temporary instrumentation is needed. +4. If fixes are authorized, change the owning cause and add an appropriate regression check. Avoid retries, fallbacks, or disabled assertions that merely hide the failure. +5. Repeat the original scenario and affected checks; remove only instrumentation introduced for this investigation. + +## Verification + +- [ ] Evidence connects the original symptom to the identified cause. +- [ ] The fix, if requested, resolves the reproduction and preserves adjacent behavior. +- [ ] Relevant regression checks pass against the final implementation. + +## Handoff and stopping condition + +Report cause, evidence, changes, and remaining uncertainty. If reproduction is unavailable, label the diagnosis provisional. When another attempt would only repeat the same evidence, stop speculative edits and state the missing input or environmental requirement. diff --git a/.agents/skills/workflow/deslop/SKILL.md b/.agents/skills/b/b-deslop/SKILL.md similarity index 80% rename from .agents/skills/workflow/deslop/SKILL.md rename to .agents/skills/b/b-deslop/SKILL.md index e49d00f2..66c70647 100644 --- a/.agents/skills/workflow/deslop/SKILL.md +++ b/.agents/skills/b/b-deslop/SKILL.md @@ -1,13 +1,20 @@ --- -name: deslop -description: Check diff against main and remove all AI generated code slop introduced in this branch. Use when the user types /deslop. +name: b-deslop +description: Check diff against main and remove all AI generated code slop introduced in this branch. Use when the user types /b-deslop. disable-model-invocation: true --- +## Purpose + Check diff against main and remove all AI generated code slop introduced in this branch. Report at the end with only a 1-3 sentence summary of what you changed. +## Steps + 1. **Check diff**: Check diff against main to see what was introduced in this branch 2. **Identify slop**: Identify AI-generated slop (extra comments that a human wouldn't add or is inconsistent with the rest of the file, extra defensive checks or try/catch blocks that are abnormal for that area of the codebase especially if called by trusted/validated codepaths, casts to any to get around type issues, any other style that is inconsistent with the file) 3. **Remove slop**: Remove all identified AI-generated slop 4. **Report**: Provide 1-3 sentence summary of what you changed +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/diagrams/SKILL.md b/.agents/skills/b/b-diagrams/SKILL.md similarity index 86% rename from .agents/skills/workflow/diagrams/SKILL.md rename to .agents/skills/b/b-diagrams/SKILL.md index cbc8c7da..e73124bc 100644 --- a/.agents/skills/workflow/diagrams/SKILL.md +++ b/.agents/skills/b/b-diagrams/SKILL.md @@ -1,13 +1,20 @@ --- -name: diagrams -description: Analyze provided code, architecture, or concept and generate clear, well-structured Mermaid diagram visualizing relationships, flow, or structure. Use when the user types /diagrams. +name: b-diagrams +description: Analyze provided code, architecture, or concept and generate clear, well-structured Mermaid diagram visualizing relationships, flow, or structure. Use when the user types /b-diagrams. disable-model-invocation: true --- +## Purpose + Analyze provided code, architecture, or concept and generate clear, well-structured Mermaid diagram visualizing relationships, flow, or structure. +## Steps + 1. **Analyze the input**: Understand what user wants to visualize (code flow, architecture, data relationships, state machines, sequences, etc) 2. **Choose appropriate diagram type**: `flowchart` (process flows/decision trees/algorithms), `sequenceDiagram` (API calls/message passing/request-response flows), `classDiagram` (class structures/inheritance/interfaces), `erDiagram` (database schemas/entity relationships), `stateDiagram-v2` (state machines/lifecycle flows), `graph TD/LR` (dependency graphs/module relationships), `gitgraph` (git branching strategies), `journey` (user journeys), `gantt` (timelines/schedules) 3. **Generate diagram**: Create with clear descriptive node labels, logical grouping with subgraphs where appropriate, consistent styling/direction, meaningful relationship labels on edges, not overly complex - split into multiple diagrams if needed 4. **Output and explain**: Always wrap diagram in mermaid code block, explain what diagram shows, offer to refine or expand specific sections, suggest alternative diagram types if applicable +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/docker-logs/SKILL.md b/.agents/skills/b/b-docker-logs/SKILL.md similarity index 74% rename from .agents/skills/workflow/docker-logs/SKILL.md rename to .agents/skills/b/b-docker-logs/SKILL.md index bb62ea00..230f2cfb 100644 --- a/.agents/skills/workflow/docker-logs/SKILL.md +++ b/.agents/skills/b/b-docker-logs/SKILL.md @@ -1,13 +1,20 @@ --- -name: docker-logs -description: Tail logs from Docker containers to check for errors and monitor application behavior. Use when the user types /docker-logs. +name: b-docker-logs +description: Tail logs from Docker containers to check for errors and monitor application behavior. Use when the user types /b-docker-logs. disable-model-invocation: true --- +## Purpose + Tail logs from Docker containers to check for errors and monitor application behavior. +## Steps + 1. **Discover running containers**: List all running containers to see what's available using `docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"` 2. **Ask which containers**: Present list and ask which container(s) they want to monitor 3. **Ask for filters**: Ask if they want any filters (errors only, last N lines, since time, etc.) 4. **Execute command**: Run `docker logs` with suitable flags based on their needs +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-exec-push/SKILL.md b/.agents/skills/b/b-exec-push/SKILL.md new file mode 100644 index 00000000..4c116592 --- /dev/null +++ b/.agents/skills/b/b-exec-push/SKILL.md @@ -0,0 +1,25 @@ +--- +name: b-exec-push +description: Implement the requested change, validate, commit, push, and create its PR. Use when the user types /b-exec-push. +disable-model-invocation: true +--- + +## Purpose and inputs + +Invocation requests the complete implementation-to-PR path. Read the task or plan, repository instructions, branch state, affected docs, and scripts. Do not broaden the request to merge or deployment. + +## Steps + +1. Inspect the working tree and current branch. If a new branch is needed, use the repository naming convention and preserve uncommitted work; do not blindly pull into a dirty checkout. +2. Implement and review the requested change in complete slices using [b-build](../b-build/SKILL.md). +3. Update affected technical docs and nearest README. Run the repository's full pre-push gate (`pnpm qa` in Basilic); diagnose failures before publishing. +4. Commit task-owned changes with [b-git-commit](../b-git-commit/SKILL.md), then push with [b-git-push](../b-git-push/SKILL.md). +5. Create or update the PR with [b-git-create-pr](../b-git-create-pr/SKILL.md). Use the repository PR template. Preserve a `BREAKING CHANGE:` footer in the body when the title uses `!` (squash uses `PR_TITLE` + `PR_BODY`). Never open an empty description. + +## Verification and handoff + +- [ ] Requested acceptance criteria and required validation pass. +- [ ] Commit and push contain only intended work, with hooks enabled. +- [ ] PR targets the correct base and explains the final behavior. + +Return the commit, branch, PR link, and checks. If a gate fails, report the blocker and the completed local work; do not present an unpublished change as shipped. diff --git a/.agents/skills/workflow/fix-compile-errors/SKILL.md b/.agents/skills/b/b-fix-compile-errors/SKILL.md similarity index 67% rename from .agents/skills/workflow/fix-compile-errors/SKILL.md rename to .agents/skills/b/b-fix-compile-errors/SKILL.md index 339b1da5..bd8816de 100644 --- a/.agents/skills/workflow/fix-compile-errors/SKILL.md +++ b/.agents/skills/b/b-fix-compile-errors/SKILL.md @@ -1,11 +1,18 @@ --- -name: fix-compile-errors -description: Analyze compilation errors and provide fixes to resolve all errors. Use when the user types /fix-compile-errors. +name: b-fix-compile-errors +description: Analyze compilation errors and provide fixes to resolve all errors. Use when the user types /b-fix-compile-errors. disable-model-invocation: true --- +## Purpose + Analyze compilation errors and provide fixes to resolve all errors. +## Steps + 1. **Identify errors**: Type mismatches/casting, missing imports/dependencies, syntax errors/malformed code, undefined variables/functions, configuration issues 2. **Fix each error**: Identify root cause, provide corrected code, explain fix, verify all errors resolved +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/fix-git-issues/SKILL.md b/.agents/skills/b/b-fix-git-issues/SKILL.md similarity index 86% rename from .agents/skills/workflow/fix-git-issues/SKILL.md rename to .agents/skills/b/b-fix-git-issues/SKILL.md index 2b54fa06..c9de6333 100644 --- a/.agents/skills/workflow/fix-git-issues/SKILL.md +++ b/.agents/skills/b/b-fix-git-issues/SKILL.md @@ -1,13 +1,20 @@ --- -name: fix-git-issues -description: Help resolve common Git problems and conflicts with step-by-step commands and explanations. Use when the user types /fix-git-issues. +name: b-fix-git-issues +description: Help resolve common Git problems and conflicts with step-by-step commands and explanations. Use when the user types /b-fix-git-issues. disable-model-invocation: true --- +## Purpose + Help resolve common Git problems and conflicts with step-by-step commands and explanations. Use global git user for any commits—never cursor/system identity. Never use `--trailer` for Co-authored-by or similar. +## Steps + 1. **Merge Conflicts**: Analyze conflicting changes/context, provide resolution strategies, suggest best approach to preserve important changes, generate resolved code maintaining functionality 2. **Branch Management**: Help with branch cleanup/organization, suggest rebasing vs merging strategies, assist with cherry-picking, guide through complex branch operations 3. **Commit History**: Help with commit message improvements, assist with squashing/organizing commits, guide through interactive rebase, fix commit authorship/metadata 4. **Repository Issues**: Help recover from detached HEAD state, assist with undoing commits/changes, guide through stash management, resolve submodule/remote issues +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/fix-github-actions/SKILL.md b/.agents/skills/b/b-fix-github-actions/SKILL.md similarity index 86% rename from .agents/skills/workflow/fix-github-actions/SKILL.md rename to .agents/skills/b/b-fix-github-actions/SKILL.md index d44b5d29..b2cb3d91 100644 --- a/.agents/skills/workflow/fix-github-actions/SKILL.md +++ b/.agents/skills/b/b-fix-github-actions/SKILL.md @@ -1,13 +1,21 @@ --- -name: fix-github-actions -description: Retrieve GitHub Actions workflow logs for the current branch PR, analyze failures, and fix CI/CD errors. Use when the user types /fix-github-actions. +name: b-fix-github-actions +description: Retrieve GitHub Actions workflow logs for the current branch PR, analyze failures, and fix CI/CD errors. Use when the user types /b-fix-github-actions. disable-model-invocation: true --- +## Purpose + Retrieve GitHub Actions workflow logs for the current branch PR, analyze failures, and fix CI/CD errors. Use **`gh`**, never GitHub MCP for Actions. +## Steps + 1. **Get current branch**: `git branch --show-current`; confirm pushed (`git status -sb`); `gh pr view` or `gh pr list --head "$(git branch --show-current)"` 2. **Retrieve Actions logs**: `gh pr checks`; `gh run list --branch "$(git branch --show-current)" --limit 10`; failed run → `gh run view --log-failed`; artifacts → `gh run download ` 3. **Analyze errors**: Parse logs for test failures, lint errors, build errors, missing dependencies, environment variables, configuration issues, timeout errors 4. **Fix issues**: Read affected files, apply fixes per project rules (TypeScript, ESLint, Biome), resolve test failures, fix build errors, add missing dependencies, update config files, address timeout issues, commit changes 5. **Verify**: Push if needed; `gh pr checks` or `gh run watch ` for the relevant workflow — do not block every push with watch (validation only) + +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/fix-push/SKILL.md b/.agents/skills/b/b-fix-push/SKILL.md similarity index 67% rename from .agents/skills/workflow/fix-push/SKILL.md rename to .agents/skills/b/b-fix-push/SKILL.md index 4ad2a2de..d8300409 100644 --- a/.agents/skills/workflow/fix-push/SKILL.md +++ b/.agents/skills/b/b-fix-push/SKILL.md @@ -1,13 +1,20 @@ --- -name: fix-push -description: Fix all reported issues, validate, commit, and push. Use when the user types /fix-push. +name: b-fix-push +description: Fix all reported issues, validate, commit, and push. Use when the user types /b-fix-push. disable-model-invocation: true --- +## Purpose + Fix all reported issues, validate, commit, and push. Use when resolving feedback or pre-push verification. Use global git user for commits—never cursor/system identity. Never use `--trailer` for Co-authored-by or similar. +## Steps + 1. **Fix issues**: Address all errors, warnings, or feedback mentioned (lint, types, tests, reviews) 2. **Validate**: Run `pnpm qa` and resolve any failures -3. **Commit**: Follow steps in @.agents/skills/workflow/git-commit/SKILL.md +3. **Commit**: Follow steps in [b-git-commit](../b-git-commit/SKILL.md) 4. **Push**: `git push` +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/fix-vercel-build/SKILL.md b/.agents/skills/b/b-fix-vercel-build/SKILL.md similarity index 73% rename from .agents/skills/workflow/fix-vercel-build/SKILL.md rename to .agents/skills/b/b-fix-vercel-build/SKILL.md index eabddd4a..ba8763e6 100644 --- a/.agents/skills/workflow/fix-vercel-build/SKILL.md +++ b/.agents/skills/b/b-fix-vercel-build/SKILL.md @@ -1,12 +1,19 @@ --- -name: fix-vercel-build -description: Retrieve Vercel build logs, analyze failures, and fix deployment issues. Use when the user types /fix-vercel-build. +name: b-fix-vercel-build +description: Retrieve Vercel build logs, analyze failures, and fix deployment issues. Use when the user types /b-fix-vercel-build. disable-model-invocation: true --- +## Purpose + Retrieve Vercel build logs, analyze failures, and fix deployment issues. MUST use Vercel MCP tools. +## Steps + 1. **Get build logs**: Use current branch (unless explicitly told otherwise), use Vercel MCP tools to retrieve build logs 2. **Analyze errors**: Parse logs for TypeScript/ESLint errors, missing dependencies, env vars, imports, config issues 3. **Fix issues**: Read affected files, apply fixes per project rules, resolve types/imports/lint errors, add missing deps, fix env/config, commit changes +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/generate-api-docs/SKILL.md b/.agents/skills/b/b-generate-api-docs/SKILL.md similarity index 80% rename from .agents/skills/workflow/generate-api-docs/SKILL.md rename to .agents/skills/b/b-generate-api-docs/SKILL.md index 339cd73d..dee37ab3 100644 --- a/.agents/skills/workflow/generate-api-docs/SKILL.md +++ b/.agents/skills/b/b-generate-api-docs/SKILL.md @@ -1,13 +1,20 @@ --- -name: generate-api-docs -description: Create comprehensive API documentation for current endpoints formatted as OpenAPI/Swagger specification or following project's documentation standards. Use when the user types /generate-api-docs. +name: b-generate-api-docs +description: Create comprehensive API documentation for current endpoints formatted as OpenAPI/Swagger specification or following project's documentation standards. Use when the user types /b-generate-api-docs. disable-model-invocation: true --- +## Purpose + Create comprehensive API documentation for current endpoints formatted as OpenAPI/Swagger specification or following project's documentation standards. +## Steps + 1. **API Overview**: Service description/purpose, base URL/versioning information, authentication/authorization requirements, rate limiting/usage policies 2. **Endpoint Documentation**: HTTP method/URL path, request parameters (path/query/body), request/response schemas with data types, example requests/responses, possible error codes/messages 3. **Data Models**: Define all data models/schemas, include field descriptions/constraints, show relationships between models, provide example payloads 4. **Usage Examples**: Common use case scenarios, SDK/client library examples, CURL command examples, error handling examples +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/generate-pr-description/SKILL.md b/.agents/skills/b/b-generate-pr-description/SKILL.md similarity index 77% rename from .agents/skills/workflow/generate-pr-description/SKILL.md rename to .agents/skills/b/b-generate-pr-description/SKILL.md index ea358e07..97af9933 100644 --- a/.agents/skills/workflow/generate-pr-description/SKILL.md +++ b/.agents/skills/b/b-generate-pr-description/SKILL.md @@ -1,14 +1,21 @@ --- -name: generate-pr-description -description: Create comprehensive pull request description based on changes in this branch, formatted as proper markdown for GitHub PR. Use when the user types /generate-pr-description. +name: b-generate-pr-description +description: Create comprehensive pull request description based on changes in this branch, formatted as proper markdown for GitHub PR. Use when the user types /b-generate-pr-description. disable-model-invocation: true --- +## Purpose + Create comprehensive pull request description based on changes in this branch, formatted as proper markdown for GitHub PR. +## Steps + 1. **Summary**: Provide clear, concise summary of what PR accomplishes 2. **Changes Made**: List key changes (code and non-code), highlight breaking changes 3. **Testing**: Describe how changes were tested, include new test cases, note manual testing performed 4. **Related Issues**: Link to related issues/tickets, use closing keywords if PR resolves issues 5. **Additional Notes**: Deployment considerations, follow-up work required, notes for reviewers +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-git-commit/SKILL.md b/.agents/skills/b/b-git-commit/SKILL.md new file mode 100644 index 00000000..1a905ba2 --- /dev/null +++ b/.agents/skills/b/b-git-commit/SKILL.md @@ -0,0 +1,25 @@ +--- +name: b-git-commit +description: Review and commit the task’s intended changes using repository conventions. Use when the user types /b-git-commit. +disable-model-invocation: true +--- + +## Purpose and inputs + +Invocation requests a commit. Inspect staged and unstaged changes and the authorized task scope before staging anything. Use the default global Git identity and repository commit rules; never bypass hooks or add attribution trailers. + +## Steps + +1. Inspect the diff and distinguish task-owned changes from unrelated work, including pre-staged files. If the intended commit cannot be separated safely, clarify the file scope. +2. Check required validation evidence and documentation updates. Resolve failures caused by this task; report unrelated blockers honestly. +3. Stage explicit task-owned paths or hunks. Do not sweep unrelated or untracked work into the commit with a blanket add. +4. Write a Conventional Commit: lowercase type/scope, imperative summary of at most 60 characters, no period. Follow repository-specific scope conventions. +5. Commit with hooks enabled; inspect the result and remaining working tree. + +## Verification and handoff + +- [ ] Commit contains only the intended change and passes required hooks. +- [ ] Documentation and validation evidence describe that change. +- [ ] Unrelated work remains intact. + +Return commit ID and concise scope. A commit request alone does not authorize pushing. diff --git a/.agents/skills/b/b-git-create-pr/SKILL.md b/.agents/skills/b/b-git-create-pr/SKILL.md new file mode 100644 index 00000000..8126ea07 --- /dev/null +++ b/.agents/skills/b/b-git-create-pr/SKILL.md @@ -0,0 +1,25 @@ +--- +name: b-git-create-pr +description: Create or update a reviewable pull request for the intended branch. Use when the user types /b-git-create-pr. +disable-model-invocation: true +--- + +## Purpose and inputs + +Invocation requests publishing the intended branch and creating its PR. Inspect the base, branch diff, relevant issue, existing PR, and repository template first. + +## Steps + +1. Ensure task-owned changes are committed and validated using the repository's checks. Use [b-git-commit](../b-git-commit/SKILL.md) if needed, preserving unrelated work. +2. Push the intended branch using [b-git-push](../b-git-push/SKILL.md). +3. Write a standalone description: problem, resulting behavior, verification evidence, and material limitations. Follow the repository template; never create an empty description. Use a conventional PR title when the repository requires it. Keep a `BREAKING CHANGE:` footer in the body when required so squash-merge preserves it. +4. Reuse an existing PR for this branch rather than duplicating it. Use known applicable labels and requested reviewers; do not invent assignments. +5. With a CLI, write multiline text to a temporary file and pass the body-file option. Verify the resulting title, base, and description. + +## Verification and handoff + +- [ ] Correct source and target branches and a nonempty, accurate description. +- [ ] Validation claims match observed results; remaining blockers are explicit. +- [ ] PR URL resolves to the intended change. + +Return the PR link. PR creation does not authorize merge or deployment. diff --git a/.agents/skills/workflow/git-pr-comments/SKILL.md b/.agents/skills/b/b-git-pr-comments/SKILL.md similarity index 84% rename from .agents/skills/workflow/git-pr-comments/SKILL.md rename to .agents/skills/b/b-git-pr-comments/SKILL.md index 5a9613c6..7a99f83a 100644 --- a/.agents/skills/workflow/git-pr-comments/SKILL.md +++ b/.agents/skills/b/b-git-pr-comments/SKILL.md @@ -1,13 +1,20 @@ --- -name: git-pr-comments -description: Process outstanding reviewer feedback, apply required fixes, and draft clear responses for each GitHub pull-request comment. Use when the user types /git-pr-comments. +name: b-git-pr-comments +description: Process outstanding reviewer feedback, apply required fixes, and draft clear responses for each GitHub pull-request comment. Use when the user types /b-git-pr-comments. disable-model-invocation: true --- +## Purpose + Process outstanding reviewer feedback, apply required fixes, and draft clear responses for each GitHub pull-request comment. Use global git user for commits—never cursor/system identity. Never use `--trailer` for Co-authored-by or similar. +## Steps + 1. **Sync and audit comments**: Pull latest branch changes, open PR conversation view and read every unresolved comment, group comments by affected files/themes 2. **Plan resolutions**: List requested code edits for each thread, identify clarifications/additional context needed, note dependencies/blockers before implementing changes 3. **Implement fixes**: Apply targeted updates addressing one comment thread at a time, run relevant tests/linters after impactful changes, stage changes with commits referencing addressed feedback 4. **Draft responses**: Summarize action taken/reasoning for each comment, link to commits/lines when clarification helps reviewers verify, highlight remaining questions/follow-up needs +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-git-push/SKILL.md b/.agents/skills/b/b-git-push/SKILL.md new file mode 100644 index 00000000..3715efe6 --- /dev/null +++ b/.agents/skills/b/b-git-push/SKILL.md @@ -0,0 +1,24 @@ +--- +name: b-git-push +description: Validate and push the intended branch while preserving unrelated work. Use when the user types /b-git-push. +disable-model-invocation: true +--- + +## Purpose and inputs + +Invocation requests pushing the current task. Inspect branch, upstream, staged/unstaged changes, and repository Git rules. Do not infer that unrelated working-tree edits belong in the push. + +## Steps + +1. Review the intended diff and required check results. Remove only temporary debug code introduced by the task. +2. If task changes need committing, follow [b-git-commit](../b-git-commit/SKILL.md). Preserve unrelated staged and untracked files. +3. Push to the inspected upstream. If none exists, use the repository's intended remote and current branch; resolve ambiguity before publishing. +4. For a rejected push, inspect divergence and use the repository's non-destructive synchronization workflow. Never force-push or bypass hooks. + +## Verification and handoff + +- [ ] Required checks and commit hooks passed. +- [ ] The intended branch was accepted by the remote. +- [ ] Unrelated local changes remain intact. + +Report branch and push result. Do not create a PR, deploy, or start watching CI unless requested. diff --git a/.agents/skills/workflow/info-architecture/SKILL.md b/.agents/skills/b/b-info-architecture/SKILL.md similarity index 86% rename from .agents/skills/workflow/info-architecture/SKILL.md rename to .agents/skills/b/b-info-architecture/SKILL.md index db7fef85..ac1aabf0 100644 --- a/.agents/skills/workflow/info-architecture/SKILL.md +++ b/.agents/skills/b/b-info-architecture/SKILL.md @@ -1,13 +1,20 @@ --- -name: info-architecture -description: Optimize information architecture following best practices to reduce cognitive load and improve discoverability. Use when the user types /info-architecture. +name: b-info-architecture +description: Optimize information architecture following best practices to reduce cognitive load and improve discoverability. Use when the user types /b-info-architecture. disable-model-invocation: true --- +## Purpose + Optimize information architecture following best practices to reduce cognitive load and improve discoverability. Restructure file names, directory hierarchies, content organization, and documentation for clarity and flow while preserving all information. +## Steps + 1. **Analyze Current Structure**: Map existing file/directory organization, identify naming inconsistencies/conflicts, find duplicated/fragmented information, assess navigation paths/discoverability 2. **Design Improved Architecture**: Group related content logically by feature/domain, apply consistent naming conventions (lowercase-with-dashes), create clear hierarchies (max 3-4 levels deep), reduce redundancy through consolidation/references 3. **Optimize Content Flow**: Order information by frequency of use/complexity, front-load critical information, use progressive disclosure for complex topics, apply clear headings/scannable structure 4. **Refactor Implementation**: Rename files/directories with descriptive/predictable names, restructure hierarchies for intuitive navigation, update cross-references/imports, consolidate duplicate content +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/light-review-existing-diffs/SKILL.md b/.agents/skills/b/b-light-review-existing-diffs/SKILL.md similarity index 79% rename from .agents/skills/workflow/light-review-existing-diffs/SKILL.md rename to .agents/skills/b/b-light-review-existing-diffs/SKILL.md index c7ce3844..4325a8bf 100644 --- a/.agents/skills/workflow/light-review-existing-diffs/SKILL.md +++ b/.agents/skills/b/b-light-review-existing-diffs/SKILL.md @@ -1,12 +1,19 @@ --- -name: light-review-existing-diffs -description: Perform quick quality pass on current diffs to surface risky areas, ensure polish, and flag follow-up actions for deeper review. Use when the user types /light-review-existing-diffs. +name: b-light-review-existing-diffs +description: Perform quick quality pass on current diffs to surface risky areas, ensure polish, and flag follow-up actions for deeper review. Use when the user types /b-light-review-existing-diffs. disable-model-invocation: true --- +## Purpose + Perform quick quality pass on current diffs to surface risky areas, ensure polish, and flag follow-up actions for deeper review. +## Steps + 1. **Scan recent changes**: List open branches or pending commits requiring review, skim side-by-side diffs focusing on new/modified files, note files/modules with large/complex edits 2. **Assess quality signals**: Watch for TODOs/debug code/commented blocks needing cleanup, verify naming/formatting/imports follow project standards, check that tests/documentation were updated when behavior changed 3. **Flag next actions**: Mark sections warranting full review or pair programming, capture questions/uncertainties to raise with author, document quick fixes you can apply immediately +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/lint-fix/SKILL.md b/.agents/skills/b/b-lint-fix/SKILL.md similarity index 71% rename from .agents/skills/workflow/lint-fix/SKILL.md rename to .agents/skills/b/b-lint-fix/SKILL.md index 4c611a3e..f58a4153 100644 --- a/.agents/skills/workflow/lint-fix/SKILL.md +++ b/.agents/skills/b/b-lint-fix/SKILL.md @@ -1,11 +1,18 @@ --- -name: lint-fix -description: Analyze and automatically fix linting issues per project coding standards. Use when the user types /lint-fix. +name: b-lint-fix +description: Analyze and automatically fix linting issues per project coding standards. Use when the user types /b-lint-fix. disable-model-invocation: true --- +## Purpose + Analyze and automatically fix linting issues per project coding standards. +## Steps + 1. **Identify issues**: Code formatting/style, unused imports/variables, missing semicolons/indentation, best practice violations, type safety issues 2. **Apply fixes**: Fix formatting/style, remove unused imports/variables, add semicolons/correct indentation, apply best practices, fix type safety, explain changes +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/lint-suite/SKILL.md b/.agents/skills/b/b-lint-suite/SKILL.md similarity index 82% rename from .agents/skills/workflow/lint-suite/SKILL.md rename to .agents/skills/b/b-lint-suite/SKILL.md index 33284df2..e6345acb 100644 --- a/.agents/skills/workflow/lint-suite/SKILL.md +++ b/.agents/skills/b/b-lint-suite/SKILL.md @@ -1,12 +1,19 @@ --- -name: lint-suite -description: Run project linters, apply fixes, and ensure codebase meets formatting/style requirements before merging changes. Use when the user types /lint-suite. +name: b-lint-suite +description: Run project linters, apply fixes, and ensure codebase meets formatting/style requirements before merging changes. Use when the user types /b-lint-suite. disable-model-invocation: true --- +## Purpose + Run project linters, apply fixes, and ensure codebase meets formatting/style requirements before merging changes. +## Steps + 1. **Execute linters**: Run standard lint command with autofix enabled if available, capture remaining errors/warnings, identify files requiring manual attention 2. **Resolve findings**: Apply targeted fixes keeping edits minimal/idiomatic, refactor repeated issues (unused variables, long functions), update configuration/suppressions only when justified 3. **Verify cleanliness**: Re-run lint command to ensure zero-issue result, spot-check key files for formatting/readability, stage changes with clear commit messages when satisfied +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/nextjs-form/SKILL.md b/.agents/skills/b/b-nextjs-form/SKILL.md similarity index 93% rename from .agents/skills/workflow/nextjs-form/SKILL.md rename to .agents/skills/b/b-nextjs-form/SKILL.md index d05ebad7..7d0052ff 100644 --- a/.agents/skills/workflow/nextjs-form/SKILL.md +++ b/.agents/skills/b/b-nextjs-form/SKILL.md @@ -1,13 +1,17 @@ --- -name: nextjs-form -description: Build production-ready Next.js 16 forms with Server Actions, progressive enhancement, comprehensive validation, and accessibility. Use when the user types /nextjs-form. +name: b-nextjs-form +description: Build production-ready Next.js 16 forms with Server Actions, progressive enhancement, comprehensive validation, and accessibility. Use when the user types /b-nextjs-form. disable-model-invocation: true --- +## Purpose + Build production-ready Next.js 16 forms with Server Actions, progressive enhancement, comprehensive validation, and accessibility. **Override:** If the app already mutates through an API client (Fastify + TanStack Query), do not introduce Server Actions, `useFormStatus`, or `useOptimistic` for those flows. +## Steps + 1. **Create shared Zod schema**: Define Zod schema for form validation, use schema for both client-side (UX) and server-side (security) validation, colocate schema with form component or in feature-specific schema file, infer TypeScript types from schema using `z.infer` 2. **Implement Server Action**: Create Server Action with `"use server"` directive, extract/validate FormData using shared Zod schema, return proper result objects with success/error states (never throw directly), use `revalidatePath`/`revalidateTag` for cache invalidation, support redirect after successful submission, ensure Server Action works with progressive enhancement 3. **Build form component**: Use `useActionState` (React 19) for form state management/error display, use `useFormStatus` for pending submit status, handle initial state/state updates from Server Actions, display validation errors with field-level/form-level feedback, implement proper form reset after successful submission, use `useOptimistic` for immediate feedback where beneficial @@ -17,3 +21,6 @@ Build production-ready Next.js 16 forms with Server Actions, progressive enhance 7. **Apply coding standards**: Follow TypeScript rules (interfaces, type inference, RORO pattern), use shadcn/ui Form components, apply mobile-first responsive design, follow linting rules (Biome + ESLint) 8. **Verify and test**: Run `pnpm lint:fix`, test form submission with JavaScript enabled/disabled, verify keyboard navigation/screen reader compatibility, test error handling/validation messages, verify cache invalidation works correctly +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-onboard-new-developer/SKILL.md b/.agents/skills/b/b-onboard-new-developer/SKILL.md new file mode 100644 index 00000000..5ac0f394 --- /dev/null +++ b/.agents/skills/b/b-onboard-new-developer/SKILL.md @@ -0,0 +1,23 @@ +--- +name: b-onboard-new-developer +description: Comprehensive onboarding process to get new developer up and running quickly. Use when the user types /b-onboard-new-developer. +disable-model-invocation: true +--- + +## Purpose + +Comprehensive onboarding process to get new developer up and running quickly. + +## Steps + +1. **Environment setup**: Install required tools, set up development environment, configure IDE/extensions, set up git/SSH keys +2. **Clone and setup**: Run `pnpm setup` at repo root +3. **Decide context**: Local only, or remote VPC? See the development-environments page from the technical docs path in `AGENTS.md`. +4. **Run web/API**: `pnpm dev` (API + Next.js) +5. **Run mobile (optional)**: `pnpm --filter @repo/mobile start` (or `start:localhost` / `start:tunnel` for remote). See the development-environments page from the technical docs path in `AGENTS.md`. +6. **Verify**: Simulator/device can load the app and reach the API +7. **Project familiarization**: Review project structure, understand architecture, read key documentation, set up local database, verify all tests passing, submit first PR + +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/optimize-performance/SKILL.md b/.agents/skills/b/b-optimize-performance/SKILL.md similarity index 83% rename from .agents/skills/workflow/optimize-performance/SKILL.md rename to .agents/skills/b/b-optimize-performance/SKILL.md index cfe525e0..a6a95951 100644 --- a/.agents/skills/workflow/optimize-performance/SKILL.md +++ b/.agents/skills/b/b-optimize-performance/SKILL.md @@ -1,12 +1,19 @@ --- -name: optimize-performance -description: Analyze current code for performance bottlenecks and provide optimization recommendations, focusing on measurable improvements while maintaining code quality/readability. Use when the user types /optimize-performance. +name: b-optimize-performance +description: Analyze current code for performance bottlenecks and provide optimization recommendations, focusing on measurable improvements while maintaining code quality/readability. Use when the user types /b-optimize-performance. disable-model-invocation: true --- +## Purpose + Analyze current code for performance bottlenecks and provide optimization recommendations, focusing on measurable improvements while maintaining code quality/readability. +## Steps + 1. **Performance Analysis**: Identify slow algorithms/inefficient data structures, find memory leaks/excessive allocations, detect unnecessary computations/redundant operations, analyze database queries/API calls 2. **Optimization Strategies**: Suggest algorithm improvements/better data structures, recommend caching strategies, propose lazy loading/pagination, identify parallel processing opportunities 3. **Implementation**: Provide optimized code with explanations, include performance impact estimates, suggest profiling/monitoring approaches, consider performance/maintainability trade-offs +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/overview/SKILL.md b/.agents/skills/b/b-overview/SKILL.md similarity index 82% rename from .agents/skills/workflow/overview/SKILL.md rename to .agents/skills/b/b-overview/SKILL.md index 8c40154e..21f2aba1 100644 --- a/.agents/skills/workflow/overview/SKILL.md +++ b/.agents/skills/b/b-overview/SKILL.md @@ -1,13 +1,20 @@ --- -name: overview -description: Generate two Mermaid diagrams to overview the product. Use when the user types /overview. +name: b-overview +description: Generate two Mermaid diagrams to overview the product. Use when the user types /b-overview. disable-model-invocation: true --- +## Purpose + Generate two Mermaid diagrams to overview the product. Track progress with todos. +## Steps + 1. **Scan and explore**: Scan and explore codebase structure, entry points, components 2. **Create user journey diagram**: Create high-level user journey diagram (5-7 nodes max, action verbs, `flowchart LR` with subgraphs, warm neutrals #F7F7F4, accent orange #F34F1D for key outcomes) 3. **Create architecture diagram**: Create architecture diagram overviewing detailed technical flow (use `sequenceDiagram` to show temporal flow between components, 4-6 participants max grouped logically, show request/response patterns) 4. **Output**: Render directly in chat (don't write files) - 2-paragraph product description, user journey diagram, architecture diagram +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/plan-architecture/SKILL.md b/.agents/skills/b/b-plan-architecture/SKILL.md similarity index 96% rename from .agents/skills/workflow/plan-architecture/SKILL.md rename to .agents/skills/b/b-plan-architecture/SKILL.md index 44448831..cdbafe2d 100644 --- a/.agents/skills/workflow/plan-architecture/SKILL.md +++ b/.agents/skills/b/b-plan-architecture/SKILL.md @@ -1,6 +1,6 @@ --- -name: plan-architecture -description: Produce a structured architectural plan with explicit goals, trade-offs, and visual diagrams. Use when the user types /plan-architecture. +name: b-plan-architecture +description: Produce a structured architectural plan with explicit goals, trade-offs, and visual diagrams. Use when the user types /b-plan-architecture. disable-model-invocation: true --- diff --git a/.agents/skills/b/b-plan-feature/SKILL.md b/.agents/skills/b/b-plan-feature/SKILL.md new file mode 100644 index 00000000..d845445e --- /dev/null +++ b/.agents/skills/b/b-plan-feature/SKILL.md @@ -0,0 +1,28 @@ +--- +name: b-plan-feature +description: Plan a feature as ordered, verifiable slices without changing implementation. Use when the user types /b-plan-feature. +disable-model-invocation: true +--- + +## Purpose and inputs + +Use the requested outcome, existing plan or issue, relevant implementation, rules, and technical docs. Planning alone does not authorize implementation or Git changes. + +## Steps + +1. State goals, non-goals, acceptance criteria, and 3–5 material assumptions. Resolve consequential ambiguity; continue with reversible details already covered by the request. +2. Inspect the affected packages, their README and scripts, and existing behavior. For durable decisions, load FIRST in the repository's prescribed order and use one primary station. +3. Divide work into the smallest complete user-visible slices. For each, name likely files, dependencies, an observable acceptance condition, and the existing command or manual check that proves it. +4. Put uncertain dependencies early. Include error paths, compatibility, generated sources, and recovery where relevant. Use a diagram only when relationships need one. +5. Save to the existing plan location or the user's chosen destination. Include Goals, Assumptions, ordered tasks, Risks/Open Questions, and References (rules, skills, docs). Do not create another backlog or overwrite another task's unfinished plan. + +## Verification + +- [ ] Each slice has a result a reviewer can observe and a concrete verification method. +- [ ] Dependencies and consequential unresolved decisions are explicit. +- [ ] Commands come from inspected scripts; generated outputs have an owning source. +- [ ] The plan is reviewable without reconstructing this conversation. + +## Handoff + +Return the plan location, unresolved decisions, and first implementable slice. If the user also requested implementation, continue within that authorization; otherwise finish with the plan. Do not create a branch or scaffold code just to plan. diff --git a/.agents/skills/workflow/refactor-code/SKILL.md b/.agents/skills/b/b-refactor-code/SKILL.md similarity index 79% rename from .agents/skills/workflow/refactor-code/SKILL.md rename to .agents/skills/b/b-refactor-code/SKILL.md index 1126cd0f..f6f82f16 100644 --- a/.agents/skills/workflow/refactor-code/SKILL.md +++ b/.agents/skills/b/b-refactor-code/SKILL.md @@ -1,12 +1,19 @@ --- -name: refactor-code -description: Refactor selected code to improve quality while maintaining same functionality, providing refactored code with explanations. Use when the user types /refactor-code. +name: b-refactor-code +description: Refactor selected code to improve quality while maintaining same functionality, providing refactored code with explanations. Use when the user types /b-refactor-code. disable-model-invocation: true --- +## Purpose + Refactor selected code to improve quality while maintaining same functionality, providing refactored code with explanations. +## Steps + 1. **Code Quality**: Extract reusable functions/components, eliminate duplication, improve naming, simplify complex logic/reduce nesting 2. **Performance**: Identify/fix bottlenecks, optimize algorithms/data structures, reduce unnecessary computations, improve memory usage 3. **Maintainability**: Make code readable/self-documenting, add appropriate comments, follow SOLID principles/design patterns, improve error handling/edge case coverage +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/b-release-review/SKILL.md b/.agents/skills/b/b-release-review/SKILL.md new file mode 100644 index 00000000..c729982e --- /dev/null +++ b/.agents/skills/b/b-release-review/SKILL.md @@ -0,0 +1,19 @@ +--- +name: b-release-review +description: Advisory review of release impact, changelog, and template contracts. Use when the user types /b-release-review. +disable-model-invocation: true +--- + +Advisory only. Do not bump versions, merge a release PR, or use publish credentials. + +## Steps + +1. **Scope**: Diff against `main` (feature PR) or the previous `v*` tag (release PR). List payload paths (`apps/api|web|mobile`, `packages/`, `tools/`, `scripts/`, `.cursor/`, `.agents/`, generator). +2. **Title**: Suggest a conventional title (`feat`/`fix`/`perf` or `!` / `BREAKING CHANGE`). Flag `docs`/`chore`/`test`/`ci`/`style` when payload paths changed unless the body has `skip-release: true`. +3. **Contract**: Note generator CLI, template include/exclude, and docs snapshot impact. Generated trees must not gain the documentation app or the generator. +4. **Notes**: Draft user-facing changelog bullets and required adopter actions. Keep Release Please's deterministic changelog authoritative. +5. **Evidence**: Link tests, classification, and (on a release PR) scaffold acceptance. Stop if a human gate is missing (npm trusted publisher, GitHub App, who may merge). + +## Completion + +Read [completion evidence](../references/completion.md); return advisory findings without changing versions or publishing. diff --git a/.agents/skills/workflow/retro/SKILL.md b/.agents/skills/b/b-retro/SKILL.md similarity index 65% rename from .agents/skills/workflow/retro/SKILL.md rename to .agents/skills/b/b-retro/SKILL.md index 5e657579..9046db5e 100644 --- a/.agents/skills/workflow/retro/SKILL.md +++ b/.agents/skills/b/b-retro/SKILL.md @@ -1,17 +1,25 @@ --- -name: retro -description: After finishing work, answer "anything you would do better in retrospect ?" Use when the user types /retro. +name: b-retro +description: After finishing work, answer "anything you would do better in retrospect ?" Use when the user types /b-retro. disable-model-invocation: true --- +## Purpose + Answer this question about the work just completed (this session, the last plan, or the diff in context): anything you would do better in retrospect ? -Reply in chat first. Then, if a decision or convention changed, **update the same-change files** (matching MDX, nearest README, FIRST overlay) in a follow-up the user can accept. Never leave a load-bearing lesson only in chat or `__dev/`. +Reply in chat only—never create files unless the user asks to apply a change. + +## Steps 1. **Scope**: The work that just shipped or the plan just executed—not a career retrospective, not a generic process lecture 2. **Be specific**: Name files, APIs, grep patterns, leftover folders, APIs you taught vs what the app still calls. Skip items that were the right call 3. **Actionable**: Each item implies a different next action (rewrite, delete, document, don't migrate). If you would not change it, omit it 4. **Honest**: Include mistakes (broken snippets, substring grep, hunting dirs that were already gone) -5. **Short**: A few bullets. No preamble. Offer to patch durable files; do not implement a rewrite unless asked +5. **Short**: A few bullets. No preamble. Do not offer to implement unless asked + +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/review-plan/SKILL.md b/.agents/skills/b/b-review-plan/SKILL.md similarity index 95% rename from .agents/skills/workflow/review-plan/SKILL.md rename to .agents/skills/b/b-review-plan/SKILL.md index b63fa7f5..b374be74 100644 --- a/.agents/skills/workflow/review-plan/SKILL.md +++ b/.agents/skills/b/b-review-plan/SKILL.md @@ -1,6 +1,6 @@ --- -name: review-plan -description: Review the attached or in-context plan. Use when the user types /review-plan. +name: b-review-plan +description: Review the attached or in-context plan. Use when the user types /b-review-plan. disable-model-invocation: true --- diff --git a/.agents/skills/b/b-roadmap/SKILL.md b/.agents/skills/b/b-roadmap/SKILL.md new file mode 100644 index 00000000..832f00a2 --- /dev/null +++ b/.agents/skills/b/b-roadmap/SKILL.md @@ -0,0 +1,22 @@ +--- +name: b-roadmap +description: Analyze codebase and generate visual roadmap of potential features and improvements. Use when the user types /b-roadmap. +disable-model-invocation: true +--- + +## Purpose + +Analyze codebase and generate visual roadmap of potential features and improvements. Track progress with todos. + +## Steps + +1. **Scan codebase**: Scan codebase architecture, patterns, conventions, look for opportunities (missing common patterns, performance optimization opportunities, developer experience improvements, user-facing feature enhancements, code quality/refactoring candidates) +2. **Identify opportunities**: Identify feature gaps and improvement opportunities +3. **Create timeline diagram**: Create phased overview showing features by effort level (Quick Wins, Medium Effort, Strategic) +4. **Create current vs proposed flowchart**: Show integration points (solid borders for existing features, dashed orange borders for proposed features) +5. **Ask user**: Ask "Would you like me to create a plan for any of these features?" (plans follow @.cursor/rules/base/general.mdc: References, assumptions, deferrals) +6. **Output**: Render directly in chat (don't write files) - brief assessment of current codebase state, prioritized feature list (5-8 items with rationale), timeline diagram, current vs proposed flowchart + +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/run-all-tests-and-fix/SKILL.md b/.agents/skills/b/b-run-all-tests-and-fix/SKILL.md similarity index 78% rename from .agents/skills/workflow/run-all-tests-and-fix/SKILL.md rename to .agents/skills/b/b-run-all-tests-and-fix/SKILL.md index d2266d9e..4216f841 100644 --- a/.agents/skills/workflow/run-all-tests-and-fix/SKILL.md +++ b/.agents/skills/b/b-run-all-tests-and-fix/SKILL.md @@ -1,12 +1,19 @@ --- -name: run-all-tests-and-fix -description: Execute full test suite and systematically fix any failures, ensuring code quality and functionality. Use when the user types /run-all-tests-and-fix. +name: b-run-all-tests-and-fix +description: Execute full test suite and systematically fix any failures, ensuring code quality and functionality. Use when the user types /b-run-all-tests-and-fix. disable-model-invocation: true --- +## Purpose + Execute full test suite and systematically fix any failures, ensuring code quality and functionality. +## Steps + 1. **Run test suite**: Execute all tests in project, capture output/identify failures, check both unit and integration tests 2. **Analyze failures**: Categorize by type (flaky, broken, new failures), prioritize fixes based on impact, check if failures related to recent changes 3. **Fix issues systematically**: Start with most critical failures, fix one issue at a time, re-run tests after each fix, verify all tests pass +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/security-audit/SKILL.md b/.agents/skills/b/b-security-audit/SKILL.md similarity index 76% rename from .agents/skills/workflow/security-audit/SKILL.md rename to .agents/skills/b/b-security-audit/SKILL.md index 49d05256..f082b607 100644 --- a/.agents/skills/workflow/security-audit/SKILL.md +++ b/.agents/skills/b/b-security-audit/SKILL.md @@ -1,12 +1,19 @@ --- -name: security-audit -description: Comprehensive security review to identify and fix vulnerabilities in codebase. Use when the user types /security-audit. +name: b-security-audit +description: Comprehensive security review to identify and fix vulnerabilities in codebase. Use when the user types /b-security-audit. disable-model-invocation: true --- +## Purpose + Comprehensive security review to identify and fix vulnerabilities in codebase. +## Steps + 1. **Dependency audit**: Check for known vulnerabilities, update outdated packages, review third-party dependencies 2. **Code security review**: Check for common vulnerabilities, review authentication/authorization, audit data handling practices 3. **Infrastructure security**: Review environment variables, check access controls, audit network security, verify dependencies updated and secure, no hardcoded secrets, input validation implemented, authentication secure, authorization properly configured +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/security-review/SKILL.md b/.agents/skills/b/b-security-review/SKILL.md similarity index 83% rename from .agents/skills/workflow/security-review/SKILL.md rename to .agents/skills/b/b-security-review/SKILL.md index a7b34079..564f0acd 100644 --- a/.agents/skills/workflow/security-review/SKILL.md +++ b/.agents/skills/b/b-security-review/SKILL.md @@ -1,13 +1,20 @@ --- -name: security-review -description: Perform comprehensive security review of current code and provide specific remediation steps with code examples for each security issue identified. Use when the user types /security-review. +name: b-security-review +description: Perform comprehensive security review of current code and provide specific remediation steps with code examples for each security issue identified. Use when the user types /b-security-review. disable-model-invocation: true --- +## Purpose + Perform comprehensive security review of current code and provide specific remediation steps with code examples for each security issue identified. +## Steps + 1. **Authentication & Authorization**: Verify proper authentication mechanisms, check authorization controls/permission systems, review session management/token handling, ensure secure password policies/storage 2. **Input Validation & Sanitization**: Identify SQL injection vulnerabilities, check for XSS/CSRF attack vectors, validate all user inputs/API parameters, review file upload/processing security 3. **Data Protection**: Ensure sensitive data encryption at rest/in transit, check for data exposure in logs/error messages, review API responses for information leakage, verify proper secrets management 4. **Infrastructure Security**: Review dependency security/known vulnerabilities, check HTTPS configuration/certificate validation, analyze CORS policies/security headers, review environment variable/configuration security +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/use-shadcn/SKILL.md b/.agents/skills/b/b-use-shadcn/SKILL.md similarity index 85% rename from .agents/skills/workflow/use-shadcn/SKILL.md rename to .agents/skills/b/b-use-shadcn/SKILL.md index b4d78638..1623058e 100644 --- a/.agents/skills/workflow/use-shadcn/SKILL.md +++ b/.agents/skills/b/b-use-shadcn/SKILL.md @@ -1,14 +1,21 @@ --- -name: use-shadcn -description: Build shadcn/ui components following monorepo structure and coding standards. Use when the user types /use-shadcn. +name: b-use-shadcn +description: Build shadcn/ui components following monorepo structure and coding standards. Use when the user types /b-use-shadcn. disable-model-invocation: true --- +## Purpose + Build shadcn/ui components following monorepo structure and coding standards. +## Steps + 1. **Use MCP servers**: Use `shadcnui-official` for single components/variants/canonical patterns, use `shadcnui-jpisnice-react` for full blocks/demos/page templates, only call MCP when unsure about implementation or encountering errors 2. **Install in `@repo/ui`**: Install components in `packages/ui/src/components/`, ensure `components.json` points to `@repo/ui/lib/utils` and `@repo/ui/components`, follow existing component organization patterns 3. **Follow monorepo import patterns**: Import from `@repo/ui/components/*` never directly from packages/ui, use `@repo/ui/lib/utils` for utilities like `cn`, import Radix primitives from `@repo/ui/radix` never directly from `@radix-ui/react-*` 4. **Apply coding standards**: Follow TypeScript rules (interfaces, type inference, RORO pattern), use class-variance-authority (cva) for variants, apply mobile-first responsive design, follow linting rules (Biome + ESLint) 5. **Verify and test**: Run `pnpm lint:fix` to ensure code quality, verify imports work correctly in consuming apps, test component functionality and responsiveness +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/use-tdd/SKILL.md b/.agents/skills/b/b-use-tdd/SKILL.md similarity index 84% rename from .agents/skills/workflow/use-tdd/SKILL.md rename to .agents/skills/b/b-use-tdd/SKILL.md index bd4a5049..9c5b2ee9 100644 --- a/.agents/skills/workflow/use-tdd/SKILL.md +++ b/.agents/skills/b/b-use-tdd/SKILL.md @@ -1,13 +1,20 @@ --- -name: use-tdd -description: Guide for implementing features using TDD workflow when explicitly requested. Use when the user types /use-tdd. +name: b-use-tdd +description: Guide for implementing features using TDD workflow when explicitly requested. Use when the user types /b-use-tdd. disable-model-invocation: true --- +## Purpose + Guide for implementing features using TDD workflow when explicitly requested. TDD follows red-green-refactor cycle: write failing tests first, implement minimal code to pass, then refactor while keeping tests green. +## Steps + 1. **Write Failing Test (Red Phase)**: Write test describing desired behavior, test should fail initially, focus on behavior not implementation, follow project testing conventions (see @.cursor/rules/base/testing.mdc), use real APIs - no mocks for core functionality 2. **Implement Minimal Code (Green Phase)**: Write simplest code that makes test pass, don't worry about code quality yet, verify test passes, avoid over-engineering 3. **Refactor (Refactor Phase)**: Improve code quality while keeping tests green, extract common patterns/improve naming/reduce duplication, ensure all tests still pass, follow project coding standards/linting rules 4. **Repeat Cycle**: Continue red-green-refactor for each feature increment, build up functionality incrementally, each cycle should be small and focused +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/use-v0/SKILL.md b/.agents/skills/b/b-use-v0/SKILL.md similarity index 91% rename from .agents/skills/workflow/use-v0/SKILL.md rename to .agents/skills/b/b-use-v0/SKILL.md index c44cc57c..284e25fe 100644 --- a/.agents/skills/workflow/use-v0/SKILL.md +++ b/.agents/skills/b/b-use-v0/SKILL.md @@ -1,14 +1,21 @@ --- -name: use-v0 -description: Generate high-quality v0.dev prompt based on current file, selection, and repository context. Use when the user types /use-v0. +name: b-use-v0 +description: Generate high-quality v0.dev prompt based on current file, selection, and repository context. Use when the user types /b-use-v0. disable-model-invocation: true --- +## Purpose + Generate high-quality v0.dev prompt based on current file, selection, and repository context. Follow validation workflow before opening v0.dev. +## Steps + 1. **Generate prompt**: Analyze current file, selection, and repository context, include constraints: Next.js App Router, React + TypeScript, Tailwind CSS, shadcn/ui, Server Components by default, client components only when strictly necessary, accessible semantic HTML, mobile-first responsive layout, clean minimal production-ready design, no data fetching or mock APIs unless explicitly required, follow existing layout/spacing/component patterns, max 300 lines per file, max 1 react component per file 2. **Show prompt to user**: Display generated v0 prompt (plain text) with OPEN/EDIT/CANCEL instruction line, ask user to validate with single explicit choice: reply "OPEN" to open v0.dev, reply "EDIT:" followed by changes to revise prompt, reply "CANCEL" to stop 3. **Handle user response**: If user replies "EDIT:", revise prompt based on feedback and repeat validation steps (1–2), if user replies "CANCEL", stop process, if user replies "OPEN", proceed to next step 4. **Validate prompt**: Before opening, ensure prompt is concise with no repo-internal paths/secrets/private tokens, prompt avoids irrelevant implementation details, URL-encoded prompt length within browser URL limits (if too long, ask user to shorten/scope it) 5. **Open v0.dev**: URL-encode final approved prompt, open browser at `https://v0.dev?chat={url_encoded_prompt}`, output only "Opened v0.dev" (do not output the URL) +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/visualize/SKILL.md b/.agents/skills/b/b-visualize/SKILL.md similarity index 76% rename from .agents/skills/workflow/visualize/SKILL.md rename to .agents/skills/b/b-visualize/SKILL.md index 28228fc8..4f594d15 100644 --- a/.agents/skills/workflow/visualize/SKILL.md +++ b/.agents/skills/b/b-visualize/SKILL.md @@ -1,13 +1,20 @@ --- -name: visualize -description: Use mermaid diagram to visualize the data lineage of the referenced code or project. Use when the user types /visualize. +name: b-visualize +description: Use mermaid diagram to visualize the data lineage of the referenced code or project. Use when the user types /b-visualize. disable-model-invocation: true --- +## Purpose + Use mermaid diagram to visualize the data lineage of the referenced code or project. +## Steps + 1. **Analyze code**: Understand the data flow, relationships, and structure in the referenced code or project 2. **Choose diagram type**: Select appropriate mermaid diagram type (flowchart, sequence diagram, class diagram, ER diagram, etc.) based on what needs visualization 3. **Generate diagram**: Create mermaid diagram with clear labels, logical grouping, consistent styling, and meaningful relationships 4. **Explain**: Explain what the diagram shows and offer to refine or expand specific sections +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/write-api-test/SKILL.md b/.agents/skills/b/b-write-api-test/SKILL.md similarity index 89% rename from .agents/skills/workflow/write-api-test/SKILL.md rename to .agents/skills/b/b-write-api-test/SKILL.md index e747b29e..ec0c41d2 100644 --- a/.agents/skills/workflow/write-api-test/SKILL.md +++ b/.agents/skills/b/b-write-api-test/SKILL.md @@ -1,14 +1,21 @@ --- -name: write-api-test -description: Create comprehensive tests for API endpoints validating behavior through external interactions. Use when the user types /write-api-test. +name: b-write-api-test +description: Create comprehensive tests for API endpoints validating behavior through external interactions. Use when the user types /b-write-api-test. disable-model-invocation: true --- +## Purpose + Create comprehensive tests for API endpoints validating behavior through external interactions. Tests use real services with sandbox/staging endpoints and dedicated test accounts following strict safety protocols. +## Steps + 1. **Test Structure**: Follow project conventions for test file naming, set up proper test lifecycle, use appropriate utilities to simulate requests, test through public interfaces only 2. **Test Coverage**: All operations (create, read, update, delete), error handling/validation scenarios, response validation against contracts, authentication/authorization, input validation 3. **Test Pattern**: Simulate requests through testing utilities, test external behavior not internal code, validate response codes/structure matches contracts, test success/failure scenarios 4. **Response Validation**: Parse/validate responses, use appropriate assertion methods, validate against defined contracts, test structure not implementation 5. **Safety Protocols**: Sandbox/staging endpoints configured (production forbidden), dedicated least-privilege test accounts used, credentials stored securely (no hardcoded secrets), rate limiting implemented and respected, resource cleanup implemented, hybrid testing strategy applied +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/write-unit-tests/SKILL.md b/.agents/skills/b/b-write-unit-tests/SKILL.md similarity index 82% rename from .agents/skills/workflow/write-unit-tests/SKILL.md rename to .agents/skills/b/b-write-unit-tests/SKILL.md index 729b9821..b1adb636 100644 --- a/.agents/skills/workflow/write-unit-tests/SKILL.md +++ b/.agents/skills/b/b-write-unit-tests/SKILL.md @@ -1,13 +1,20 @@ --- -name: write-unit-tests -description: Create comprehensive unit tests for current code with proper imports/setup per project testing conventions. Use when the user types /write-unit-tests. +name: b-write-unit-tests +description: Create comprehensive unit tests for current code with proper imports/setup per project testing conventions. Use when the user types /b-write-unit-tests. disable-model-invocation: true --- +## Purpose + Create comprehensive unit tests for current code with proper imports/setup per project testing conventions. +## Steps + 1. **Test Coverage**: Test all public methods/functions, cover edge cases/error conditions, test positive/negative scenarios, aim for high coverage 2. **Test Structure**: Use project's testing framework conventions, write clear descriptive test names, follow Arrange-Act-Assert pattern, group related tests logically 3. **Test Cases**: Happy path scenarios, edge cases/boundary conditions, error handling/exception cases, mock external dependencies appropriately 4. **Test Quality**: Make tests independent/isolated, ensure deterministic/repeatable, keep simple/focused, add helpful assertion messages +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/workflow/yolo/SKILL.md b/.agents/skills/b/b-yolo/SKILL.md similarity index 92% rename from .agents/skills/workflow/yolo/SKILL.md rename to .agents/skills/b/b-yolo/SKILL.md index 0ac9bf8f..316cf299 100644 --- a/.agents/skills/workflow/yolo/SKILL.md +++ b/.agents/skills/b/b-yolo/SKILL.md @@ -1,11 +1,15 @@ --- -name: yolo -description: Execute complete quality assurance pass across all apps in monorepo. Use when the user types /yolo. +name: b-yolo +description: Execute complete quality assurance pass across all apps in monorepo. Use when the user types /b-yolo. disable-model-invocation: true --- +## Purpose + Execute complete quality assurance pass across all apps in monorepo. Performs comprehensive checks including linting, building, testing, runtime verification, AI-assisted code review, and external review remediation when available. Work autonomously using best judgment, strictly following repository rules, Cursor rules, and established best practices. NEVER ask permission to edit, delete, or create files - proceed directly with all file operations. +## Steps + 1. **Run Quality Checks**: Execute linting (`pnpm lint`) for all apps/packages, run builds (`pnpm build`) to verify compilation, execute test suites (`pnpm test`), verify dev runtime works correctly, fix any issues found 2. **AI-Assisted Code Review**: Perform full AI-based static review, analyze for correctness bugs/type safety issues/runtime edge cases/performance pitfalls/architectural inconsistencies, cross-check findings against Cursor rules/indexed documentation/existing code patterns, fix all high-confidence issues 3. **External Review Consumption (Conditional)**: If available, fetch existing external review feedback (CodeRabbit PR comments via MCP, CI annotations), categorize issues by severity (critical/correctness/security/performance/style), apply fixes complying with Cursor rules/repository conventions, document conflicts between external feedback and local rules @@ -14,3 +18,6 @@ Execute complete quality assurance pass across all apps in monorepo. Performs co 6. **Iteration and Verification**: Iterate as many times as needed until all checks pass, re-run quality checks after fixes to verify resolution, ensure no regressions introduced 7. **Summary and Reporting**: Provide comprehensive summary, document issues found/fixes applied/issues deferred and reasons, note follow-up recommendations +## Completion + +Read [completion evidence](../references/completion.md) before reporting completion. diff --git a/.agents/skills/b/references/authoring.md b/.agents/skills/b/references/authoring.md new file mode 100644 index 00000000..1f5ad2f2 --- /dev/null +++ b/.agents/skills/b/references/authoring.md @@ -0,0 +1,18 @@ +# Workflow authoring pattern + +One outcome per slash playbook. Keep discovery descriptions to capability and invocation; detailed steps belong in the body. The frontmatter name equals its folder, starts with `b-`, and retains `disable-model-invocation: true`. + +Use these sections when they change execution decisions: + +1. **Purpose and inputs**: when to use it, required context, and whether it plans, inspects, edits, or publishes. +2. **Steps**: inspect, act within scope, and verify; keep task-specific mechanics here. +3. **Verification**: short, observable exit conditions. Link shared completion evidence instead of copying a global policy. +4. **Handoff**: the artifact or evidence returned, uncertainty, and next action. Include a stopping condition for investigative loops. + +Small existing specialty playbooks may use Purpose, Steps, and Completion. Add detailed checklists when improving their behavior, not merely to fill headings. Supporting files belong inside `b/references/` so installing `b` carries them. Other catalogs are optional context, never install-time dependencies. + +Avoid fixed interview quotas, mandatory plans for trivial edits, fabricated performance estimates, auto-commits from implementation, and silent expansion from review into fixes. Do not encode arbitrary line-count, timing, coverage, or security thresholds as repository policy. + +Validate packaging with `pnpm validate` in the catalog. Exercise realistic prompts: empty/unknown dispatch, review-only, implementation-only, failed verification, and a dirty working tree before commit. Structural validation proves packaging, not agent behavior. + +Structure and evidence-oriented checklist ideas were informed by [Addy Osmani's agent-skills](https://github.com/addyosmani/agent-skills/tree/84ee50673804b95c287d1e4eb4f1c1dad7c5188a), especially its skill anatomy, planning, debugging, and review guidance. These are Basilic-specific instructions, not a wholesale installation. See the catalog adoption analysis for tradeoffs. diff --git a/.agents/skills/b/references/completion.md b/.agents/skills/b/references/completion.md new file mode 100644 index 00000000..8f7ee3fa --- /dev/null +++ b/.agents/skills/b/references/completion.md @@ -0,0 +1,20 @@ +# Completion evidence + +Use the rows relevant to the requested deliverable. Mark passed, failed, not run, or not applicable with a short reason; do not turn an unchecked box into a claim. Repository-specific gates remain authoritative. + +| Deliverable | Evidence to include | +|---|---| +| Plan | Observable acceptance conditions, ordered dependencies, known risks, and references to inspected sources | +| Review or diagnosis | Inspected scope, concrete findings or reproduction evidence, and unresolved uncertainty; no edits unless requested | +| Implementation | Changed behavior, affected checks against the final change, original scenario where relevant, and documentation updates | +| Commit or push | Intended diff, required checks/hooks, commit or remote result, and preserved unrelated work | +| Pull request | Correct branch/base, standalone description, observed verification, and PR link | + +## Common failure checks + +- [ ] A successful type check is not being presented as runtime proof. +- [ ] Checks were not disabled, weakened, or reported as passing after an environmental failure. +- [ ] Fixes address the evidenced cause; retries and fallback values do not hide broken assumptions. +- [ ] Only requested external actions were taken; finishing implementation does not imply publishing permission. + +For changed behavior, follow the repository's existing documentation and FIRST destinations. This file records evidence; it does not define another product success metric, quality policy, or backlog. diff --git a/.agents/skills/f/SKILL.md b/.agents/skills/f/SKILL.md index 2acec295..032b59d0 100644 --- a/.agents/skills/f/SKILL.md +++ b/.agents/skills/f/SKILL.md @@ -1,6 +1,6 @@ --- name: f -description: FIRST factory dispatcher. Lists /f-* station and role skills. Use when the user types /f or /f-product, /f-designer, /f-analyst. +description: FIRST factory dispatcher. Lists /f-* station and role skills. Use when the user types /f or /f-product, /f-journeys, /f-analyst. disable-model-invocation: true --- @@ -13,15 +13,13 @@ If `$ARGUMENTS` (or the rest of the message after `/f`) is a known token, Read ` ## Aliases → folder - `product` → `f-product` -- `design`, `designer` → `f-designer` +- `journeys`, `design`, `designer` → `f-journeys` - `data`, `analyst` → `f-analyst` - `documentation`, `docs`, `ia` → `f-info-architect` - `ai`, `ai-expert` → `f-ai-expert` -- `journeys` → `f-journeys` - `architecture` → `f-architecture` - `api` → `f-api` -- `workflow` → `f-workflow` -- `pipelines` → `f-pipelines` +- `workflow`, `pipelines` → `f-workflow` - `quality` → `f-quality` - `security` → `f-security` - `operations` → `f-operations` @@ -33,17 +31,15 @@ Unknown token: say so, print this index, stop. | Slash | Station or role | |---|---| | `/f-product` | Product | -| `/f-journeys` | Journeys | -| `/f-designer` | Design | +| `/f-journeys` | Journeys (`/f-designer` is an alias) | | `/f-architecture` | Architecture | | `/f-analyst` | Data | | `/f-api` | API | | `/f-info-architect` | Documentation | -| `/f-workflow` | Workflow | -| `/f-pipelines` | Pipelines | +| `/f-workflow` | Workflow (`/f-pipelines` is an alias) | | `/f-quality` | Quality | | `/f-security` | Security | | `/f-operations` | Operations | -| `/f-ai-expert` | Agents, evals, assistant (not a 13th station) | +| `/f-ai-expert` | Agents, evals, assistant (not an eleventh station) | Always load `./references/analyst.md` before the child spec. diff --git a/.agents/skills/f/f-ai-expert/SKILL.md b/.agents/skills/f/f-ai-expert/SKILL.md index 044240e3..aa79a843 100644 --- a/.agents/skills/f/f-ai-expert/SKILL.md +++ b/.agents/skills/f/f-ai-expert/SKILL.md @@ -4,7 +4,7 @@ description: Inspect agents, evals, and assistant surfaces as a data analyst. Us disable-model-invocation: true --- -Role only. Not a thirteenth station. +Role only. Not an eleventh station. 1. Read [../references/analyst.md](../references/analyst.md). 2. Read `_first/FIRST.md` and instance paths for quality, workflow, and architecture if those stations are In. diff --git a/.agents/skills/f/f-analyst/references/spec.md b/.agents/skills/f/f-analyst/references/spec.md index b15a1fb2..9bb0a9d5 100644 --- a/.agents/skills/f/f-analyst/references/spec.md +++ b/.agents/skills/f/f-analyst/references/spec.md @@ -10,7 +10,7 @@ I treat data as durable product state with meaning, ownership, and a lifecycle ## Outcome -Core domain concepts have shared names and definitions. Each important dataset has an owner and authoritative source. Identity, constraints, retention, deletion, lineage, and schema evolution are explicit at the level the product needs. Migrations preserve or deliberately transform meaning. Copies do not silently become competing sources of truth. +Core domain concepts have shared names and definitions. Each important dataset has an owner and authoritative source. Identity, constraints, retention, deletion, lineage, and schema evolution are explicit at the level the product needs. Session and long-term conversational memory are backing-service state with an owner, retention, and deletion — not process memory and not eval datasets. Migrations preserve or deliberately transform meaning. Copies do not silently become competing sources of truth. ## Artifacts @@ -73,7 +73,9 @@ Propose the smallest useful data decision. Implement constraints and migrations **Data vs Product:** Product owns event taxonomy and the outcomes to measure. Data owns the meaning, authority, and lifecycle of the resulting records. -**Data vs Architecture:** Architecture places stores and data flows. Data defines what the state means and which source is authoritative. +**Data vs Architecture:** Architecture places stores and requires a stateless process. Data defines what the state means and which source is authoritative, including conversational memory in a backing store. + +**Data vs Journeys:** Journeys owns resume and turn-taking in the job. Data owns the memory store. **Data vs API:** Data owns the canonical domain model. API owns the external representation and compatibility contract. diff --git a/.agents/skills/f/f-api/references/spec.md b/.agents/skills/f/f-api/references/spec.md index 69272e75..ac936ad1 100644 --- a/.agents/skills/f/f-api/references/spec.md +++ b/.agents/skills/f/f-api/references/spec.md @@ -6,7 +6,7 @@ Define the capability and its boundary before consumers couple to an accidental ## Statement -I treat the capability and its boundary as a design decision, not an implementation leftover. Before a second consumer depends on a shape, I want to know what goes in, what comes out, what fails, which Security-owned authorization requirement applies, and how denial appears. That contract might be HTTP, a typed module, an event, a CLI, or an agent tool. The format matters less than making the boundary explicit on purpose. +I treat the capability and its boundary as a design decision, not an implementation leftover. Before a second consumer depends on a shape, I want to know what goes in, what comes out, what fails, which Security-owned authorization requirement applies, and how denial appears. That contract might be HTTP, a typed module, an event, a CLI, or an agent tool. Export the capability as a self-contained boundary — a bound port or equivalent — rather than as a leak of the process that happens to implement it. The format matters less than making the boundary explicit on purpose. ## Outcome diff --git a/.agents/skills/f/f-architecture/references/spec.md b/.agents/skills/f/f-architecture/references/spec.md index 69ac47e1..3558473c 100644 --- a/.agents/skills/f/f-architecture/references/spec.md +++ b/.agents/skills/f/f-architecture/references/spec.md @@ -10,7 +10,7 @@ I treat architecture as the small set of decisions that are expensive to reverse ## Outcome -The system has an inspectable structural model at the level its scale requires. Components have named responsibilities and owners. Dependency direction, external systems, data stores, and deployment units are visible. Consequential choices have rationale and known tradeoffs. Implementation conforms, or the model is updated deliberately. +The system has an inspectable structural model at the level its scale requires. Components have named responsibilities and owners. Dependency direction, external systems, data stores, and deployment units are visible. Application processes are stateless; durable state lives in attached backing services that can be swapped without rewriting the app. Consequential choices have rationale and known tradeoffs. Implementation conforms, or the model is updated deliberately. ## Artifacts @@ -74,8 +74,10 @@ Propose the smallest useful structural change or artifact. Record rationale and **Architecture vs API:** Architecture decides which components communicate. API defines the contract across a boundary. -**Architecture vs Pipelines:** Architecture describes deployment units and topology. Pipelines build, validate, and deliver them. +**Architecture vs Workflow:** Architecture describes deployment units and topology. Workflow builds, validates, and delivers them. **Architecture vs Operations:** Architecture names the running parts. Operations observes and recovers them. +**Architecture vs Documentation:** If context-engineering is a structural boundary, it is application structure, not a notebook string. Documentation versions the prompt files. + **Navigation:** [Human essay](../articles/ARCHITECTURE.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-designer/SKILL.md b/.agents/skills/f/f-designer/SKILL.md deleted file mode 100644 index a00729d6..00000000 --- a/.agents/skills/f/f-designer/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: f-designer -description: Apply Design First as a designer who inspects this repo as a data analyst. Use when the user types /f-designer or /f design. -disable-model-invocation: true ---- - -Apply Design First as a designer. - -1. Read [../references/analyst.md](../references/analyst.md). -2. Read `_first/FIRST.md`. Take the In path for `design:`. If that station is Out, stop and say so. -3. Read the instance file at that path. -4. Read [references/spec.md](references/spec.md). -5. Compare the spec and overlay to the implementation. Label Fact, inference, assumption, unresolved. -6. Propose the smallest overlay (or brief) update. Do not invent product facts. Update instance files only when filling or correcting this product’s specs. diff --git a/.agents/skills/f/f-designer/references/spec.md b/.agents/skills/f/f-designer/references/spec.md deleted file mode 100644 index f7587ec2..00000000 --- a/.agents/skills/f/f-designer/references/spec.md +++ /dev/null @@ -1,80 +0,0 @@ -# Design First - -## Principle - -Decide how the product behaves and communicates through its interface before layout and components become accidental consequences of implementation. - -## Statement - -Useful software is the floor. The product people remember is the one that feels clear, thoughtful, and enjoyable to use. I treat design as behavior, hierarchy, states, copy, and motion — not decoration bolted on at the end. Before I add a pattern, I look at what the project already has and reuse it. Invention is for when the problem actually needs it. - -## Outcome - -The interface follows a coherent design system: tokens, primitives, composition rules, and content patterns. Loading, empty, error, success, and disabled states are defined. New UI reuses established patterns. The agent-readable identity and the implementation agree, or the discrepancy is named. Browser verification across states is the bar. - -## Artifacts - -- `DESIGN.md` when Design is in — [DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md) (Google Labs, alpha): optional YAML frontmatter (tokens) plus markdown rationale. Tokens are the values; prose is how to apply them. -- Design system in code: CSS variables, Tailwind `@theme`, component library -- UI conventions and component definitions -- Interaction specifications and interface state definitions -- Accessibility requirements beyond token contrast -- Content and copy patterns -- Motion guidelines (duration, easing, when to animate) - -Prefer `_first/DESIGN.md` so the file lives in the user pack. Point `FIRST.md` at that one file. A root `DESIGN.md` only if existing tooling requires it. Never two palettes. - -If `DESIGN.md` exists: YAML tokens are the values to use; markdown do's and don'ts are constraints. Follow the spec’s `##` order when those sections apply: Overview, Colors, Typography, Layout, Elevation & Depth, Shapes, Components, Do's and Don'ts. Omit unused visual sections with YAML `omitted` and a reason — do not invent a palette to complete the template. Unknown headings are preserved; use them for FIRST concerns the Google file does not own (interface states, copy, motion, accessibility beyond contrast). Do not fork a competing schema. - -Token groups may follow W3C Design Tokens (DTCG) conceptually. Export to Tailwind `@theme` or `tokens.json` if the project already does. Use the project's pinned lint command, or `npx @google/design.md lint _first/DESIGN.md` (explicit path) when the CLI is already part of the toolchain. It reports broken token references and contrast warnings on component color pairs. A zero exit code is not an accessibility pass or browser verification. - -Do not keep a palette in `DESIGN.md` and a different one in CSS. - -## Minimum Useful Artifact - -- user goal and required interface states -- existing tokens, primitives, and patterns to reuse -- interaction and content behavior for each state -- keyboard, contrast, focus, and reduced-motion constraints -- browser views or notes showing how the states were verified - -## Recipe - -1. Inspect `DESIGN.md` if present, tokens in code, the component library, and adjacent screens. -2. Understand the states the feature needs: default, loading, empty, error, success, disabled. -3. Identify gaps: missing states, values outside the system, DESIGN.md/code drift. -4. Propose primitives from the existing system. New components only when nothing fits. -5. Define interaction: click, submit, dismiss, navigate back. Add motion only where it improves feedback. -6. Implement with existing primitives. Do not introduce hex values or typefaces outside the system. -7. Validate in the browser across states, keyboard, and reduced motion. Lint DESIGN.md if the project has the CLI. -8. Update design artifacts if you introduced a pattern worth reusing. - -## Validation - -- New UI uses existing tokens and components unless a gap is documented. -- `DESIGN.md` and the implementation agree, or the discrepancy is named. -- All required states render and behave correctly. -- Motion respects `prefers-reduced-motion`. -- A green DESIGN.md lint is not browser verification. - -## Definition of Done - -Interface behavior matches design intent. States are handled. Patterns are reusable or documented. Tokens in the agent-readable file and in code are the same system. - -## Agent Prompt - -Apply Design First to this repository. - -Read `DESIGN.md` if it exists, then design system documentation, UI conventions, journey flows, and existing components before building new interface. Inspect how similar features are implemented. Treat YAML tokens as the values to use and markdown do's and don'ts as constraints. - -Reuse established primitives. Do not invent new components, hex values, or typefaces without checking the design system first. If tokens live in CSS or Tailwind and `DESIGN.md` disagrees or is absent, do not silently create a second palette. - -Define loading, empty, error, and success states. Use motion for feedback, not decoration. Respect reduced motion. Validate in the browser across states. A screenshot of the default view is not verification. Update durable design artifacts when patterns change. - -## Notes - -**Design vs Journeys:** Journeys define what happens. Design defines how the interface expresses it. - -**Design vs API:** API is the capability boundary. Design is the human-visible expression. `DESIGN.md` is an artifact, not the principle — parallel to OpenAPI versus API First. - -**Navigation:** [Human essay](../articles/DESIGN.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-info-architect/references/spec.md b/.agents/skills/f/f-info-architect/references/spec.md index 90439e76..87dbbe34 100644 --- a/.agents/skills/f/f-info-architect/references/spec.md +++ b/.agents/skills/f/f-info-architect/references/spec.md @@ -10,7 +10,7 @@ I document decisions and context, not obvious code. If someone will need to redi ## Outcome -Consequential decisions, conventions, setup steps, and domain context live in discoverable project files. Documentation matches current behavior or explicitly notes drift. When behavior or assumptions change, the files change in the same work. +Consequential decisions, conventions, setup steps, domain context, prompt templates, and behavioral specs live in discoverable project files. Documentation matches current behavior or explicitly notes drift. When behavior or assumptions change, the files change in the same work. ## Artifacts @@ -19,6 +19,7 @@ Consequential decisions, conventions, setup steps, and domain context live in di - Architecture documentation and ADRs - Project conventions and operational notes - Domain documentation and setup guides +- Prompt templates, behavioral specs, and context-engineering notes as versioned project files - Changelog entries for behavior changes worth remembering - `llms.txt` for selective public-site orientation when the product publishes substantial web documentation @@ -70,6 +71,6 @@ Document decisions and conventions — not obvious code. Preserve intentional ex **Documentation vs everything:** Other stations produce decisions. Documentation decides which context must stay durable. -**Documentation vs Workflow:** Workflow determines when context is created and handed off. Documentation preserves it. +**Documentation vs Workflow:** Workflow determines when context is created and handed off, and runs CI. Documentation preserves it, including prompt files. Quality already owns eval datasets. Architecture already owns application structure if context-engineering is a structural boundary. **Navigation:** [Human essay](../articles/DOCUMENTATION.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-journeys/SKILL.md b/.agents/skills/f/f-journeys/SKILL.md index 5e022af5..d4bdcc6c 100644 --- a/.agents/skills/f/f-journeys/SKILL.md +++ b/.agents/skills/f/f-journeys/SKILL.md @@ -1,13 +1,13 @@ --- name: f-journeys -description: Apply Journeys First with a journeys lens who inspects this repo as a data analyst. Use when the user types /f-journeys or /f journeys. +description: Apply Journeys First with a journeys lens who inspects this repo as a data analyst. Use when the user types /f-journeys, /f journeys, /f-designer, or /f design. disable-model-invocation: true --- Apply Journeys First with a journeys lens. 1. Read [../references/analyst.md](../references/analyst.md). -2. Read `_first/FIRST.md`. Take the In path for `journeys:`. If that station is Out, stop and say so. +2. Read `_first/FIRST.md`. Take the In path for `journeys:`. If that station is Out, stop and say so. Tokens `design` and `designer` resolve here; they are not a station. 3. Read the instance file at that path. 4. Read [references/spec.md](references/spec.md). 5. Compare the spec and overlay to the implementation. Label Fact, inference, assumption, unresolved. diff --git a/.agents/skills/f/f-journeys/references/spec.md b/.agents/skills/f/f-journeys/references/spec.md index fc196516..0ba585ca 100644 --- a/.agents/skills/f/f-journeys/references/spec.md +++ b/.agents/skills/f/f-journeys/references/spec.md @@ -2,15 +2,15 @@ ## Principle -Map how someone finishes a job — including errors, permissions, and state — before implementation invents the path from whichever screen shipped first. +Map how someone finishes a job — including errors, permissions, state, and how the interface expresses it — before implementation invents the path from whichever screen shipped first. ## Statement -The product is not a collection of screens. It is someone trying to finish a job. I map that job from entry to completion before I trust implementation to fill in the gaps. Happy paths are cheap. The product breaks in the alternates: the error nobody designed, the permission that exists on one route but not another, the state with no exit. +The product is not a collection of screens. It is someone trying to finish a job. I map that job from entry to completion before I trust implementation to fill in the gaps. Happy paths are cheap. The product breaks in the alternates: the error nobody designed, the permission that exists on one route but not another, the state with no exit. How those states communicate — copy, motion, tokens, primitives — is the same station, not a later polish pass. ## Outcome -Actors, entry points, happy paths, alternates, error paths, permission gates, and completion criteria are documented or explicitly marked unknown. Missing states are visible before code hardens around an incomplete model. +Actors, entry points, happy paths, alternates, error paths, permission gates, and completion criteria are documented or explicitly marked unknown. Missing states are visible before code hardens around an incomplete model. The interface follows a coherent system: tokens, primitives, composition rules, and content patterns. Loading, empty, error, success, and disabled states are defined. New UI reuses established patterns. Browser verification across states is the bar. ## Artifacts @@ -19,6 +19,18 @@ Actors, entry points, happy paths, alternates, error paths, permission gates, an - Actor definitions (human, admin, agent, system) - Acceptance criteria tied to completion - Permission and error matrices for critical flows +- `DESIGN.md` when Journeys includes a visual identity file — [DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md) (Google Labs, alpha): optional YAML frontmatter (tokens) plus markdown rationale. Tokens are the values; prose is how to apply them. Parallel to OpenAPI versus API First. +- Design system in code: CSS variables, Tailwind `@theme`, component library +- Interaction specifications, content patterns, and motion guidelines +- Accessibility requirements beyond token contrast + +Prefer `_first/DESIGN.md` so the file lives in the user pack. Point `FIRST.md` at that one file as a Journeys artifact, not as a station. A root `DESIGN.md` only if existing tooling requires it. Never two palettes. + +If `DESIGN.md` exists: YAML tokens are the values to use; markdown do's and don'ts are constraints. Follow the spec’s `##` order when those sections apply. Omit unused visual sections with YAML `omitted` and a reason. Unknown headings are preserved; use them for interface states, copy, motion, and accessibility beyond contrast. Do not fork a competing schema. + +Token groups may follow W3C Design Tokens (DTCG) conceptually. Use the project's pinned lint command, or `npx -p @google/design.md designmd lint _first/DESIGN.md` (explicit path) when the CLI is not already part of the toolchain. A zero exit code is not an accessibility pass or browser verification. + +Do not keep a palette in `DESIGN.md` and a different one in CSS. ## Minimum Useful Artifact @@ -27,46 +39,54 @@ Actors, entry points, happy paths, alternates, error paths, permission gates, an - important alternate, error, cancel, and recovery paths - permission gates that reference Security-owned policy - completion criteria and explicit deferrals +- user goal and required interface states +- existing tokens, primitives, and patterns to reuse +- keyboard, contrast, focus, and reduced-motion constraints +- browser views or notes showing how the states were verified ## Recipe -1. Inspect product goals, existing routes, screens, API flows, and auth rules. -2. Understand the actor and the job they are trying to finish — not the screen, the outcome. -3. Identify gaps: missing error exits, permission drift across entry points, states with no resume. -4. Propose the smallest useful map: one job, its entries, its gates, its completion. -5. Write happy path, then alternates, then errors, then permission gates. -6. Compare the map to implementation. Flag missing or contradictory states. -7. Validate that a reviewer can trace every mapped state to implemented behavior or an explicit deferral. -8. Update journey artifacts when flow behavior changes. +1. Inspect product goals, existing routes, screens, API flows, auth rules, `DESIGN.md` if present, tokens in code, and the component library. +2. Understand the actor and the job they are trying to finish — not the screen, the outcome. Name the interface states that job needs: default, loading, empty, error, success, disabled. +3. Identify gaps: missing error exits, permission drift across entry points, states with no resume, values outside the system, DESIGN.md/code drift. +4. Propose the smallest useful map: one job, its entries, its gates, its completion, primitives from the existing system. +5. Write happy path, then alternates, then errors, then permission gates. Define interaction. Add motion only where it improves feedback. +6. Compare the map to implementation. Flag missing or contradictory states. Do not introduce hex values or typefaces outside the system. +7. Validate that a reviewer can trace every mapped state to implemented behavior or an explicit deferral. Validate in the browser across states, keyboard, and reduced motion. Lint DESIGN.md if the project has the CLI. +8. Update journey and design artifacts when flow or interface behavior changes. ## Validation - Every critical flow has defined error and recovery behavior. - Permission checks are consistent across entry points. - Every mapped state traces to implemented behavior or an explicit deferral; accidental gaps fail validation. +- New UI uses existing tokens and components unless a gap is documented. +- `DESIGN.md` and the implementation agree, or the discrepancy is named. +- Motion respects `prefers-reduced-motion`. +- A green DESIGN.md lint is not browser verification. ## Definition of Done -Critical flows are mapped with happy, alternate, and error paths documented or explicitly deferred. Implementation matches the map, or the map was updated to reflect a deliberate change. +Critical flows are mapped with happy, alternate, and error paths documented or explicitly deferred. Interface behavior matches intent. States are handled. Implementation matches the map, or the map was updated to reflect a deliberate change. ## Agent Prompt Apply Journeys First to this repository. -Read product documentation, journey artifacts if they exist, and the current implementation of user-facing flows. Map actors, entry points, states, permissions, errors, and completion criteria. Compare documentation to actual behavior. +Read product documentation, journey artifacts if they exist, `DESIGN.md` if it exists, and the current implementation of user-facing flows. Map actors, entry points, states, permissions, errors, completion criteria, and how those states are expressed. Compare documentation to actual behavior. Treat YAML tokens as the values to use and markdown do's and don'ts as constraints. -Do not invent UI. Describe what happens and why. Surface missing states and edge cases before implementing. Preserve intentional existing flow decisions. Propose the smallest useful update to journey artifacts. Update durable project files when flow behavior changes. +Do not invent a second product language. Describe what happens and why, then reuse established primitives. Surface missing states and edge cases before implementing. Preserve intentional existing flow and interface decisions. If tokens live in CSS or Tailwind and `DESIGN.md` disagrees or is absent, do not silently create a second palette. -Treat agents in the product as actors in the flow. If an actor is unnamed, do not invent a tool for them. +Treat agents in the product as actors in the flow. If an actor is unnamed, do not invent a tool for them. Validate in the browser across states. A screenshot of the default view is not verification. Update durable project files when flow or interface behavior changes. ## Notes -**Journeys vs Product:** Product answers what and why. Journeys answer how someone finishes. - -**Journeys vs Design:** Journeys describe behavior and flow. Design describes how that behavior is expressed through the interface. +**Journeys vs Product:** Product answers what and why. Journeys answer how someone finishes and how that job is expressed. **Journeys vs Security:** Journeys show where a permission gate occurs and what the actor experiences. Security owns the permission policy. -**Journeys vs Data:** Journeys describe actor-visible states and transitions. Data owns the canonical meaning and lifecycle of persisted state. +**Journeys vs Data:** Journeys describe actor-visible states and transitions, including conversational resume. Data owns the canonical meaning and lifecycle of persisted state, including the memory store. + +**Journeys vs API:** API is the capability boundary. Journeys is the human-visible expression of the job. `DESIGN.md` is an artifact, not a station — parallel to OpenAPI versus API First. **Navigation:** [Human essay](../articles/JOURNEYS.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-operations/references/spec.md b/.agents/skills/f/f-operations/references/spec.md index 6b19649b..5f846122 100644 --- a/.agents/skills/f/f-operations/references/spec.md +++ b/.agents/skills/f/f-operations/references/spec.md @@ -10,7 +10,7 @@ Shipping is not the end of engineering work. It is when the system meets reality ## Outcome -Production behavior is observable at the level the project needs. Logs are structured and useful. Metrics and alerts cover critical failure modes. Runbooks exist for known recovery paths. Product analytics are not the same dashboard as engineering health. A green pipeline after a fix is not verified recovery. +Production behavior is observable at the level the project needs. Logs are structured and useful. A healthy HTTP status is not AI quality: log prompt, response, tool, and token signals at project scale. Metrics and alerts cover critical failure modes. Runbooks exist for known recovery paths. Product analytics are not the same dashboard as engineering health. User-feedback hooks are ops instrumentation, not Product funnels. A green pipeline after a fix is not verified recovery. ## Artifacts @@ -59,20 +59,22 @@ Apply Operations First to this repository. Read operational documentation, logging configuration, metrics, alerts, and runbooks before changing runtime behavior. Inspect how the system reports errors and health. Identify blind spots in observability. -Propose the smallest useful logging, metric, or alert improvement. When diagnosing production issues, use structured signals — not guesswork. Route fixes through the project's workflow and pipelines. Verify recovery after deploy in the running system. +Propose the smallest useful logging, metric, or alert improvement. When diagnosing production issues, use structured signals — not guesswork. Route fixes through the project's workflow. Verify recovery after deploy in the running system. Product analytics — events, funnels, activation — belong with Product First. Do not file a missing success event as an ops ticket. Do not add observability complexity beyond project scale. Production agents need identity, logs, retries, and a trace of what they did. ## Notes -**Operations vs Pipelines:** Pipelines move changes into production. Operations understands and runs the system after deployment. +**Operations vs Workflow:** Workflow moves changes into production. Operations understands and runs the system after deployment. **Operations vs Security:** Security defines trust and protection. Operations defines visibility and recovery. -**Operations vs Product:** Product owns whether the bet is working. Operations owns whether the system is healthy. Same warehouse is fine. Same dashboard is not. +**Operations vs Product:** Product owns whether the bet is working and already owns success events. Operations owns whether the system is healthy. Same warehouse is fine. Same dashboard is not. Do not put funnels on the ops dashboard. **Operations vs Architecture:** Architecture names deployment units and topology. Operations observes, supports, and recovers them. **Operations vs Data:** Data owns product-state meaning and lineage. Operations owns telemetry about runtime health. +**Operations vs Quality:** Quality already owns scored evals. Operations logs the runtime signals that make a 200 with a useless answer visible. + **Navigation:** [Human essay](../articles/OPERATIONS.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-pipelines/SKILL.md b/.agents/skills/f/f-pipelines/SKILL.md deleted file mode 100644 index ad279f5d..00000000 --- a/.agents/skills/f/f-pipelines/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: f-pipelines -description: Apply Pipelines First with a pipelines lens who inspects this repo as a data analyst. Use when the user types /f-pipelines or /f pipelines. -disable-model-invocation: true ---- - -Apply Pipelines First with a pipelines lens. - -1. Read [../references/analyst.md](../references/analyst.md). -2. Read `_first/FIRST.md`. Take the In path for `pipelines:`. If that station is Out, stop and say so. -3. Read the instance file at that path. -4. Read [references/spec.md](references/spec.md). -5. Compare the spec and overlay to the implementation. Label Fact, inference, assumption, unresolved. -6. Propose the smallest overlay (or brief) update. Do not invent product facts. Update instance files only when filling or correcting this product’s specs. diff --git a/.agents/skills/f/f-pipelines/references/spec.md b/.agents/skills/f/f-pipelines/references/spec.md deleted file mode 100644 index e51dbba0..00000000 --- a/.agents/skills/f/f-pipelines/references/spec.md +++ /dev/null @@ -1,77 +0,0 @@ -# Pipelines First - -## Principle - -Treat automated validation and delivery as part of the development feedback loop — including the agent feedback loop — not as a ritual after the real work. - -## Statement - -A change is not done when it compiles on my machine. It is done when it passes the same checks everyone else relies on, and when it can reach the environment it needs to reach. For agents, CI is ground truth: implement, run checks, read failures, fix or escalate, run again. I keep the path from source to deployable explicit at the level the project actually needs. - -## Outcome - -Changes flow through an automated path: format → lint → typecheck → test → build → preview (if applicable) → deploy → verify. The commit stage builds the artifact once. Later stages promote that artifact; they do not rebuild a different one per environment. Agents can interpret CI failure output and act. - -## Artifacts - -- CI workflow definitions -- Deployment workflows and build configuration -- Preview environment setup when the project has it -- Release checks and deployment documentation -- Local scripts that mirror CI where useful -- The commit-stage artifact (the thing that gets promoted) - -Keep infrastructure proportional. A static site does not need an orchestration platform. - -## Minimum Useful Artifact - -- triggers and ordered validation stages -- exact local commands that mirror automated checks -- commit-stage artifact identity and storage -- promotion path across environments without rebuilding -- readable failure ownership and post-deploy verification step - -## Recipe - -1. Inspect the current pipeline: what runs, when, on what triggers. -2. Understand the path from change to deployable artifact. Find where the binary or bundle is built. -3. Identify gaps: missing checks, manual steps that should be automated, rebuild-per-environment, unreadable logs, flaky jobs. -4. Propose the smallest pipeline improvement — one check, one workflow, one script. -5. Implement. Keep proportional to project size. Build once; promote that artifact. -6. Verify locally with the same commands CI uses when possible. -7. Read CI output. Fix failures or escalate infrastructure limits. Unreadable logs are a pipeline bug. -8. Document deploy and release steps if they are not already durable. - -## Validation - -- CI passes on the change branch. -- Local validation commands match CI behavior. -- The commit-stage artifact is what later stages deploy. -- A green agent sandbox is not a substitute for the project's pipeline. -- Deploy path is documented and was exercised or dry-run where safe. - -## Definition of Done - -The change is validated by automated pipelines and is deployable through the project's defined path. Pipeline config is updated if new checks were added. - -## Agent Prompt - -Apply Pipelines First to this repository. - -Read CI workflows, build configuration, and deployment documentation before changing delivery mechanics. Inspect what validation runs on each change. Use the project's existing lint, test, and build commands — mirror CI locally when possible. - -When implementation completes, ensure CI would pass. Read failure logs and fix or escalate. Do not treat a green local sandbox as the pipeline. Do not add pipeline complexity beyond what the project needs. Do not rebuild a different artifact per environment if the project already builds once. - -Distinguish pipeline failures from quality criteria failures — fix the code or the gate, not both at once without intent. Update deployment docs when delivery behavior changes. - -## Notes - -**Pipelines vs Workflow:** Pipelines automate validation and delivery. Workflow defines how humans and agents respond to those signals. - -**Pipelines vs Architecture:** Architecture defines deployment units and topology. Pipelines build and promote those units. - -**Pipelines vs Quality:** Quality names the bar. Pipelines run it. - -**Pipelines vs Operations:** Pipelines get changes into production. Operations runs what arrived. - -**Navigation:** [Human essay](../articles/PIPELINES.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-product/references/spec.md b/.agents/skills/f/f-product/references/spec.md index e632fd7d..2e21b20d 100644 --- a/.agents/skills/f/f-product/references/spec.md +++ b/.agents/skills/f/f-product/references/spec.md @@ -81,7 +81,7 @@ Propose the smallest useful update to product artifacts, including the tracking ## Notes -**Product vs Journeys:** Product names what, why, and how we will know. Journeys name how someone finishes. +Product owns what and why. Journeys owns how someone finishes the job and how the interface expresses it. Do not treat completion paths as implementation detail outside Journeys. **Product vs Quality:** Product names the outcome after use. Quality names the bar that gates a release. diff --git a/.agents/skills/f/f-quality/references/spec.md b/.agents/skills/f/f-quality/references/spec.md index c76b7c4b..a18dc2d4 100644 --- a/.agents/skills/f/f-quality/references/spec.md +++ b/.agents/skills/f/f-quality/references/spec.md @@ -14,7 +14,7 @@ Acceptance criteria exist for features that matter. Tests protect critical paths ## Artifacts -- Acceptance criteria tied to journeys and product goals +- Acceptance criteria tied to product goals and user-visible completion - Unit, integration, and end-to-end tests - AI evals and golden datasets where relevant - Performance budgets and regression checks @@ -48,11 +48,11 @@ Choose the mechanism that matches the output. Deterministic code gets tests. Mod - Critical paths have automated protection where the project supports it. - Failures produce actionable signal, not noise. - AI evals cover the behaviors users actually depend on. -- CI going green is Pipelines. Meeting the bar is Quality. +- CI going green is Workflow. Meeting the bar is Quality. ## Definition of Done -This station’s stated quality criteria are met and verified by the project's existing validation. Regressions are caught or explicitly accepted with documented rationale. CI going green is Pipelines. This is not Product success after use. +This station’s stated quality criteria are met and verified by the project's existing validation. Regressions are caught or explicitly accepted with documented rationale. CI going green is Workflow. This is not Product success after use. ## Agent Prompt @@ -66,7 +66,7 @@ Preserve intentional existing test patterns. Propose the smallest useful quality ## Notes -**Quality vs Pipelines:** Quality defines what should be validated. Pipelines run the validation. +**Quality vs Workflow:** Quality defines what should be validated. Workflow runs the validation. **Quality vs Product:** Product defines success after use and ships the analytics. Quality defines the bar that gates a release. diff --git a/.agents/skills/f/f-security/references/spec.md b/.agents/skills/f/f-security/references/spec.md index 3a158644..adceb5bd 100644 --- a/.agents/skills/f/f-security/references/spec.md +++ b/.agents/skills/f/f-security/references/spec.md @@ -10,7 +10,7 @@ Security is not a phase at the end. It is a set of decisions about boundaries: w ## Outcome -Trust boundaries are documented. Auth rules are consistent and enforced at boundaries. Secrets are not committed and not logged. External inputs are validated. Agent permissions are scoped: read-only where possible, destructive actions gated, secrets minimized, human approval for high-risk operations. +Trust boundaries are documented. Auth rules are consistent and enforced at boundaries. Secrets are not committed and not logged. External inputs are validated. Agent permissions are scoped: read-only where possible, destructive actions gated, secrets minimized, human approval for high-risk operations. Defense in depth on the agent: sanitize untrusted text, constrain outputs, scope tools by principal, least privilege on the agent identity. Prompt injection is one surface, not the whole principle. ## Artifacts @@ -67,10 +67,12 @@ Propose the smallest useful security fix or documentation update. Use existing p **Security vs Operations:** Security defines trust, protection, and permissions. Operations defines runtime visibility and recovery. -**Security vs API:** API defines contracts at boundaries. Security defines who may invoke them and what they may access. +**Security vs API:** API defines contracts at boundaries and shows denial at the contract. Security defines who may invoke them and what they may access. **Security vs Architecture:** Architecture maps trust boundaries and dependencies. Security defines the protection and authorization policy across them. **Security vs Data:** Data maps classification, copies, retention, and deletion. Security owns access and protection policy. +**Security vs Journeys:** Journeys places the permission gate in the job. Security owns the policy and the agent guardrails. + **Navigation:** [Human essay](../articles/SECURITY.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/f/f-workflow/SKILL.md b/.agents/skills/f/f-workflow/SKILL.md index 004df20f..dd84e02c 100644 --- a/.agents/skills/f/f-workflow/SKILL.md +++ b/.agents/skills/f/f-workflow/SKILL.md @@ -1,6 +1,6 @@ --- name: f-workflow -description: Apply Workflow First with a workflow lens who inspects this repo as a data analyst. Use when the user types /f-workflow or /f workflow. +description: Apply Workflow First with a workflow lens who inspects this repo as a data analyst. Use when the user types /f-workflow, /f workflow, /f-pipelines, or /f pipelines. disable-model-invocation: true --- diff --git a/.agents/skills/f/f-workflow/references/spec.md b/.agents/skills/f/f-workflow/references/spec.md index df6f5ac7..26283169 100644 --- a/.agents/skills/f/f-workflow/references/spec.md +++ b/.agents/skills/f/f-workflow/references/spec.md @@ -2,22 +2,24 @@ ## Principle -Make the path from intent to validated change explicit enough that humans, agents, and automation can cooperate without reconstructing the process every time. +Make the path from intent to a validated, deployable change explicit enough that humans, agents, and automation can cooperate without reconstructing the process every time. ## Statement -I care less about which methodology name is on the wall and more about whether work can move from idea to shipped, validated change. Who decides what? Where does state live? When does a human approve? If the path is only in people's heads, agents cannot help and humans cannot scale. +I care less about which methodology name is on the wall and more about whether work can move from idea to shipped, validated change. Who decides what? Where does state live? When does a human approve? A change is not done when it compiles on my machine. It is done when it passes the same checks everyone else relies on, and when it can reach the environment it needs to reach. If the path is only in people's heads, agents cannot help and humans cannot scale. ## Outcome -Work flows through a recognizable path: idea → plan → implement → review → pipeline signals → approval → release → learning. Handoffs have inputs and outputs. Work state lives in issues, tasks, or PRs — not only in chat. Human approval is explicit for destructive, security-sensitive, or product-consequential changes. +Work flows through a recognizable path: idea → plan → implement → review → pipeline signals → approval → release → learning. Handoffs have inputs and outputs. Work state lives in issues, tasks, or PRs — not only in chat. Human approval is explicit for destructive, security-sensitive, or product-consequential changes. Changes flow through an automated path: format → lint → typecheck → test → build → preview (if applicable) → deploy → verify. The commit stage builds the artifact once. Later stages promote that artifact; they do not rebuild a different one per environment. Agents can interpret CI failure output and act. ## Artifacts - Issues, tasks, and pull requests as work state - Plans and review comments - ADRs and project docs for consequential decisions -- CI results as automated validation signals +- CI workflow definitions and deployment configuration +- Preview environment setup when the project has it +- The commit-stage artifact (the thing that gets promoted) - Agent notes that affect future work, promoted to durable context - Status updates (ephemeral coordination) @@ -28,24 +30,29 @@ Routing: - Work state → issue, task, or PR - Automated validation → CI result +Keep infrastructure proportional. A static site does not need an orchestration platform. + ## Minimum Useful Artifact - intent, owner, and visible work-state location - short plan and acceptance criteria for non-trivial work - actors plus the input and output of each handoff - required human gates and who can approve -- validation, release, and learning destinations +- triggers and ordered validation stages +- exact local commands that mirror automated checks +- commit-stage artifact identity and promotion path without rebuilding +- readable failure ownership, release, and learning destinations ## Recipe -1. Inspect current work state: issues, PRs, branches, CI status, and any plan. -2. Understand the actor for this step: human, agent, CI, or production signal. -3. Identify gaps: missing plan, missing owner, approval that lives only in chat, state that cannot be found. -4. Propose before implementing on non-trivial work. Surface assumptions. -5. Implement in small, reviewable chunks. Keep state in the issue or PR. -6. Hand off to review with enough context for the next actor to continue. -7. Run validation through existing pipelines. Respond to failures — fix or escalate. -8. Obtain approval for consequential changes. Release. Capture learning in files when decisions changed. +1. Inspect current work state: issues, PRs, branches, CI status, any plan, and the path from change to deployable artifact. +2. Understand the actor for this step: human, agent, CI, or production signal. Find where the binary or bundle is built. +3. Identify gaps: missing plan, missing owner, approval that lives only in chat, missing checks, rebuild-per-environment, unreadable logs. +4. Propose before implementing on non-trivial work. Surface assumptions. Prefer the smallest pipeline improvement — one check, one workflow, one script. +5. Implement in small, reviewable chunks. Keep state in the issue or PR. Build once; promote that artifact. +6. Hand off to review with enough context for the next actor to continue. Verify locally with the same commands CI uses when possible. +7. Run validation through existing checks. Read CI output. Fix failures or escalate. Unreadable logs are a workflow bug. +8. Obtain approval for consequential changes. Release. Capture learning in files when decisions changed. Document deploy steps if they are not already durable. ## Validation @@ -53,25 +60,35 @@ Routing: - Handoffs include enough context for the next actor to continue. - Consequential decisions are in files, not only in merged code. - Failed validation routes to a clear owner and next action. +- CI passes on the change branch. +- Local validation commands match CI behavior. +- The commit-stage artifact is what later stages deploy. +- A green agent sandbox is not a substitute for the project's checks. ## Definition of Done -The change moved through an explicit path. State is updated. Durable context reflects what was decided. The next actor can continue without reconstruction. +The change moved through an explicit path and is deployable through the project's defined path. State is updated. Durable context reflects what was decided. The next actor can continue without reconstruction. ## Agent Prompt Apply Workflow First to this repository. -Read current work state — issues, PRs, plans — and project workflow documentation before acting. Identify actors, handoffs, approval boundaries, and where durable state lives. Do not rely on chat as the system of record. +Read current work state — issues, PRs, plans — plus CI workflows, build configuration, and deployment documentation before acting. Identify actors, handoffs, approval boundaries, and where durable state lives. Do not rely on chat as the system of record. + +Take notes in durable project files when discoveries affect future work. Propose before implementing on non-trivial work. Implement in reviewable chunks. Use existing CI and review paths. Mirror CI locally when possible. -Take notes in durable project files when discoveries affect future work. Propose before implementing on non-trivial work. Implement in reviewable chunks. Use existing CI and review paths. +When implementation completes, ensure CI would pass. Read failure logs and fix or escalate. Do not treat a green local sandbox as the pipeline. Do not rebuild a different artifact per environment if the project already builds once. -Stop and ask a human for product scope, security-sensitive changes, and destructive operations. Update issues, PRs, and documentation as work progresses. Preserve intentional existing process decisions. +Stop and ask a human for product scope, security-sensitive changes, and destructive operations. Distinguish pipeline failures from quality criteria failures — fix the code or the gate, not both at once without intent. Update issues, PRs, and documentation as work progresses. Preserve intentional existing process decisions. ## Notes -**Workflow vs Pipelines:** Workflow is how actors respond. Pipelines are the automated format, test, build, and deploy mechanics. +**Workflow vs Quality:** Quality names the bar. Workflow runs it. + +**Workflow vs Operations:** Workflow gets changes into production. Operations runs what arrived. A green pipeline is not verified recovery. **Workflow vs Documentation:** Workflow determines when context is created. Documentation preserves it. +**Workflow vs Architecture:** Architecture defines deployment units and topology. Workflow builds and promotes those units. + **Navigation:** [Human essay](../articles/WORKFLOW.md) · [Factory map](../ABOUT.md) diff --git a/.agents/skills/next-v16/SKILL.md b/.agents/skills/next-v16/SKILL.md index dd5e10f0..8fff9cc6 100644 --- a/.agents/skills/next-v16/SKILL.md +++ b/.agents/skills/next-v16/SKILL.md @@ -68,7 +68,7 @@ Next.js 16 App Router (GA October 2025). Prefer the version-matched docs in `nod - React 19 concurrent UI: [vercel-react-v1](../vercel-react-v1/SKILL.md) - Client data after hydration: [tanstack-query-v5](../tanstack-query-v5/SKILL.md) -- Forms that stay on the Next server: [workflow/nextjs-form](../workflow/nextjs-form/SKILL.md) +- Forms that stay on the Next server: [b/b-nextjs-form](../b/b-nextjs-form/SKILL.md) ## Two cache models (do not mix) diff --git a/.agents/skills/workflow/SKILL.md b/.agents/skills/workflow/SKILL.md deleted file mode 100644 index 10566b04..00000000 --- a/.agents/skills/workflow/SKILL.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: workflow -description: Basilic slash playbooks (plan, git, review, debug). Nested under this folder. Use when the user types /workflow or a playbook name such as /exec-push or /git-commit. -disable-model-invocation: true ---- - -Playbooks are sibling folders (`/SKILL.md`). Cursor loads them as `/`. Claude Code: read `.//SKILL.md` for the named playbook. - -FIRST station skills (`/f-product`, `/f-designer`, …) are a separate catalog: [`npx skills add blockmatic/first`](https://github.com/blockmatic/first). Do not fork that tree here. diff --git a/.agents/skills/workflow/add-documentation/SKILL.md b/.agents/skills/workflow/add-documentation/SKILL.md deleted file mode 100644 index 9ffd1258..00000000 --- a/.agents/skills/workflow/add-documentation/SKILL.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -name: add-documentation -description: Add comprehensive documentation for current code/feature per project standards (README, docs site, or inline comments). Use when the user types /add-documentation. -disable-model-invocation: true ---- - -Add documentation for the current code/feature. Follow `.cursor/rules/base/docs.mdc` and `.cursor/rules/base/readme.mdc`. - -## Where to write - -| Layer | Role | Update when | -| --- | --- | --- | -| `_first/basilic/PRODUCT.md` | Intent, feature map, roadmap | Goals, shipped vs intended, horizons changed | -| `apps/docu/content/docs/` MDX | Architecture, ADRs, how-to, Product Ready | Behavior, architecture, commands, conventions, or workflow changed | -| GitHub Issues | Backlog | Later bets and work items — not `__dev/`, not `BACKLOG.md` | -| `__dev/` | Scratch | Notes that have not graduated. Not Fact | -| `.cursor/rules` | Short constraints | A convention the agent must not violate changed | -| Nearest README | How to run this app/package; links only | Scripts, setup, or package purpose changed | - -Inline comments only when the code is otherwise misleading. Do not copy MDX into rules or READMEs. Do not `@`-attach MDX. There is no 13th FIRST station and no root `PRODUCT.md` / `ROADMAP.md`. - -1. **Identify the topic**: Matching docs section, then existing topic page (create a page only if none fits) -2. **Write or patch** `_first/basilic/PRODUCT.md` for intent, or MDX if the canonical technical explanation changed -3. **Patch README** only for run/setup/scripts; link the MDX -4. **Patch the glob-matched `.mdc`** only if a constraint changed -5. **Open or update a GitHub Issue** if the item is backlog, not documentation diff --git a/.agents/skills/workflow/clarify-task/SKILL.md b/.agents/skills/workflow/clarify-task/SKILL.md deleted file mode 100644 index a6414a11..00000000 --- a/.agents/skills/workflow/clarify-task/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: clarify-task -description: Before doing any coding work on the task, ask clarifying questions using multiple choice format to gather requirements around data flow, architecture, APIs, integrations, authentication, edge cases,. Use when the user types /clarify-task. -disable-model-invocation: true ---- - -Before doing any coding work on the task, ask clarifying questions using multiple choice format to gather requirements around data flow, architecture, APIs, integrations, authentication, edge cases, error handling, and UI/UX expectations. - -1. **Gather context**: Read readme, docs, skills, rules before asking questions -2. **Ask clarifying questions**: Use 2-4 multiple choice questions to clarify requirements (data flow and architecture, APIs and integrations, authentication/authorization, edge cases and error handling, UI/UX expectations if applicable) -3. **Restate requirements**: After receiving answers, restate final requirements in your own words to confirm understanding -4. **Confirm before proceeding**: Only then ask if they want to proceed to planning/implementation. Defer to user for ambiguous, high-risk decisions. - diff --git a/.agents/skills/workflow/code-review/SKILL.md b/.agents/skills/workflow/code-review/SKILL.md deleted file mode 100644 index 6079521d..00000000 --- a/.agents/skills/workflow/code-review/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: code-review -description: Perform thorough code review verifying functionality, maintainability, and security. Use when the user types /code-review. -disable-model-invocation: true ---- - -Perform thorough code review verifying functionality, maintainability, and security. Focus on architecture, readability, performance, provide actionable suggestions. - -1. **Understand change**: Read PR description/issues for context, identify scope of files/features impacted, note assumptions/questions -2. **Validate functionality**: Confirm code delivers intended behavior, exercise edge cases/guard conditions, check error handling/logging -3. **Assess quality**: Ensure functions focused/names descriptive/code readable, watch for duplication/dead code/missing tests, verify documentation updated -4. **Review security/risk**: Look for injection points/insecure defaults/missing validation, confirm secrets not exposed, evaluate performance/scalability impacts - diff --git a/.agents/skills/workflow/debug-browser/SKILL.md b/.agents/skills/workflow/debug-browser/SKILL.md deleted file mode 100644 index 7234d70a..00000000 --- a/.agents/skills/workflow/debug-browser/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: debug-browser -description: Iterate on the current task using debug instrumentation and browser tools without asking the user to test manually. Use when the user types /debug-browser. -disable-model-invocation: true ---- - -Iterate on the current task using debug instrumentation and browser tools without asking the user to test manually. Add debug traces, reproduce issues using browser tools, analyze traces, identify problems, and make fixes until resolved. - -1. **Add debug traces**: Add debug.log traces to key locations in the code -2. **Reproduce issue**: Use the browser tools (browser_navigate, browser_click, browser_snapshot, etc.) to reproduce the issue and collect trace output -3. **Analyze and fix**: Analyze the traces, identify the problem, and make a fix -4. **Repeat**: Repeat until the issue is resolved - diff --git a/.agents/skills/workflow/debug-issue/SKILL.md b/.agents/skills/workflow/debug-issue/SKILL.md deleted file mode 100644 index fe2d5be6..00000000 --- a/.agents/skills/workflow/debug-issue/SKILL.md +++ /dev/null @@ -1,13 +0,0 @@ ---- -name: debug-issue -description: Help debug current issue by walking through debugging process systematically and providing clear, actionable solutions. Use when the user types /debug-issue. -disable-model-invocation: true ---- - -Help debug current issue by walking through debugging process systematically and providing clear, actionable solutions. - -1. **Problem Analysis**: Identify specific problem/error, understand expected vs actual behavior, trace execution flow to find root cause -2. **Debugging Strategy**: Add appropriate logging statements, suggest debugging tools/techniques, identify key variables/states to monitor, recommend breakpoint locations -3. **Solution Approach**: Propose potential fixes with explanations, consider multiple solution approaches, evaluate trade-offs, provide step-by-step resolution plan -4. **Prevention**: Suggest ways to prevent similar issues, recommend additional tests/checks, identify code patterns that could be improved - diff --git a/.agents/skills/workflow/exec-push/SKILL.md b/.agents/skills/workflow/exec-push/SKILL.md deleted file mode 100644 index 718d6401..00000000 --- a/.agents/skills/workflow/exec-push/SKILL.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: exec-push -description: Execute plan or instructions, validate, commit, push, and create a PR. Use when the user types /exec-push. -disable-model-invocation: true ---- - -Execute plan or instructions, validate, commit, push, and create a PR. Use when implementing features, resolving feedback, or pre-push verification. Use global git user for commits—never cursor/system identity. Never use `--trailer` for Co-authored-by or similar. - -1. **Branch**: If on `main`, run `git pull origin main`, then create a new branch (`git checkout -b `). Skip if already on a branch. -2. **Execute**: Implement the plan/instructions—follow `.cursor/rules`, `apps/docu/content/docs/` topic MDX, and relevant skills -3. **Docs**: If behavior, commands, or conventions changed, update matching MDX and nearest README in the same change -4. **Validate**: Run `pnpm qa` and resolve any failures -5. **Commit**: Follow steps in @.agents/skills/workflow/git-commit/SKILL.md -6. **Push**: `git push` -7. **Pull Request**: If a PR does not already exist for your branch, open a new PR without a description. Skip if a PR already exists for this branch. diff --git a/.agents/skills/workflow/git-commit/SKILL.md b/.agents/skills/workflow/git-commit/SKILL.md deleted file mode 100644 index 826b9d5e..00000000 --- a/.agents/skills/workflow/git-commit/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: git-commit -description: Create short, focused commit message and commit staged changes. Use when the user types /git-commit. -disable-model-invocation: true ---- - -Create short, focused commit message and commit staged changes. Never use `--no-verify`. Never use `--trailer` for Co-authored-by or similar. Fix all automated review errors and warnings before committing. Use the default global git user (`git config --global user.name`, `git config --global user.email`) for all commits—never cursor/system identity. - -1. **Review changes**: Check diff `git diff --cached` (staged) or `git diff` (unstaged), understand what changed and why -2. **Docs**: If behavior, commands, or conventions changed, update matching MDX and nearest README before staging—or one line why they did not change -3. **Fix errors and warnings**: Resolve all lint, type-check, and test failures -4. **Stage changes (if not already staged)**: `git add -A` -5. **Create short commit message**: Follow [Conventional Commits](https://www.conventionalcommits.org/). Format: `(): ` or `: (): `. Type and scope lowercase (e.g. `feat(fastify):`). Short summary lowercase, imperative mood (use "fix", "add", "update" not "fixed", "added", "updated"), <= 60 chars, no period. Scope by app (`next`, `fastify`, `docu`), package (`ui`, `core`, `utils`), or omit if general. - diff --git a/.agents/skills/workflow/git-create-pr/SKILL.md b/.agents/skills/workflow/git-create-pr/SKILL.md deleted file mode 100644 index 79b102e3..00000000 --- a/.agents/skills/workflow/git-create-pr/SKILL.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: git-create-pr -description: Create well-structured pull request with proper description, labels, and reviewers. Use when the user types /git-create-pr. -disable-model-invocation: true ---- - -Create well-structured pull request with proper description, labels, and reviewers. Use global git user for any commits—never cursor/system identity. Never use `--trailer` for Co-authored-by or similar. - -1. **Prepare branch**: Ensure all changes committed, push branch to remote, verify branch up to date with main -2. **Write PR description**: Summarize changes clearly, include context/motivation, list breaking changes, add screenshots if UI changes -3. **Set up PR**: Create PR with descriptive title, add appropriate labels, assign reviewers, link related issues - diff --git a/.agents/skills/workflow/git-push/SKILL.md b/.agents/skills/workflow/git-push/SKILL.md deleted file mode 100644 index d122fe04..00000000 --- a/.agents/skills/workflow/git-push/SKILL.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -name: git-push -description: Push current branch to origin and sync with remote updates, including all changes such as untracked files, after removing any debug instrumentations. Use when the user types /git-push. -disable-model-invocation: true ---- - -Push current branch to origin and sync with remote updates, including all changes such as untracked files, after removing any debug instrumentations. Never use `--no-verify`. Never use `--trailer` for Co-authored-by or similar. Fix all automated review errors and warnings before committing. Use the default global git user (`git config --global user.name`, `git config --global user.email`) for all commits—never cursor/system identity. - -1. **Check changes**: Check diff `git diff --cached` (staged) or `git diff` (unstaged), understand what will be pushed -2. **Remove debug code**: Remove all debug instrumentations added before pushing -3. **Fix errors and warnings**: Resolve all lint, type-check, and test failures -4. **Stage and commit**: Stage changes with `git add -A`, create short commit per [Conventional Commits](https://www.conventionalcommits.org/): `(): `, type and scope lowercase, summary imperative mood ≤60 chars, no period. Scope by app (`next`, `fastify`, `docu`), package (`ui`, `core`, `utils`), or omit if general. -5. **Push**: `git push` - diff --git a/.agents/skills/workflow/onboard-new-developer/SKILL.md b/.agents/skills/workflow/onboard-new-developer/SKILL.md deleted file mode 100644 index 287f7e5f..00000000 --- a/.agents/skills/workflow/onboard-new-developer/SKILL.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: onboard-new-developer -description: Comprehensive onboarding process to get new developer up and running quickly. Use when the user types /onboard-new-developer. -disable-model-invocation: true ---- - -Comprehensive onboarding process to get a new developer up and running quickly. Product Ready is this fork-and-run path, not CI green. Canonical checklist: `apps/docu/content/docs/testing/product-ready.mdx`. - -1. **Environment setup**: Install required tools, set up development environment, configure IDE/extensions, set up git/SSH keys (Node 24, pnpm from `packageManager`) -2. **Clone and setup**: Run `pnpm setup` at repo root (install, hooks, env templates, Docker/Supabase CLI, Playwright) -3. **Decide context**: Local only, or remote VPC? (Remote VPC = dev machine in cloud; trade-offs: networking, access, cost, latency. See `apps/docu/content/docs/development/dev-environments.mdx`.) -4. **Start local Postgres**: `pnpm --filter @repo/api db:start` (`setup` does not start Supabase) -5. **Reset and seed**: `pnpm reset` from repo root (Supabase reset + Drizzle migrate + seed). See `apps/api/README.md` and ADR 008 -6. **Run web/API**: `pnpm dev` (API + Next.js) -7. **First login**: Copied env has `ALLOW_TEST=true`. Use `test@test.ai`. Signed-in home is `/`. Resend is optional. -8. **Run mobile (optional)**: `pnpm --filter @repo/mobile start` (or `start:localhost` / `start:tunnel` for remote). See `apps/docu/content/docs/development/dev-environments.mdx` -9. **Verify**: Web loads, API `GET /health` succeeds, simulator/device can reach the API if running mobile -10. **Project familiarization**: Review `_first/basilic/PRODUCT.md` and architecture MDX; run tests when changing code; submit first PR diff --git a/.agents/skills/workflow/plan-feature/SKILL.md b/.agents/skills/workflow/plan-feature/SKILL.md deleted file mode 100644 index 46a7bd3b..00000000 --- a/.agents/skills/workflow/plan-feature/SKILL.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: plan-feature -description: Systematically set up new feature from planning through implementation structure. Use when the user types /plan-feature. -disable-model-invocation: true ---- - -Systematically set up new feature from planning through implementation structure. - -1. **List goals first**: Capture and display feature goals at the outset for user alignment before any planning. Output a ## Goals section (3–7 bullets) summarizing scope, success criteria, and constraints. Confirm or adjust with user before proceeding. -2. **Gather context**: Matching glob rule and skill, then **Read** the topic MDX under `apps/docu/content/docs/` (or this repo’s docs path). Rules override skills. FIRST: `_first/AGENTS.md` → `_first/ABOUT.md` → `_first/FIRST.md` → `/f-*` → instance. -3. **Define requirements**: Clarify scope, identify user stories/acceptance criteria, plan technical approach -4. **Summarize assumptions**: List 3–5 bullets before detailed planning -5. **Create feature branch**: Branch from main/develop, set up local dev, configure dependencies -6. **Plan architecture**: Design data models/APIs, plan UI components/flow, consider testing strategy, document requirements -7. **Add diagrams**: For architecture, data flow, or component relationships, generate Mermaid diagrams. Use `flowchart` (process flows), `sequenceDiagram` (API/request flows), `classDiagram` (structures), `erDiagram` (DB schemas), `stateDiagram-v2` (lifecycles). Clear labels, subgraphs for grouping, wrap in mermaid code blocks. Split into multiple diagrams if complex. -8. **Output structure**: ## Goals first, then body, then ## References. List paths to each rule, skill, and MDX page used (plain paths, no `@`). Example: `apps/docu/content/docs/architecture/api.mdx`, `.cursor/rules/backend/fastify.mdc`, `.agents/skills/fastify-v5`. -9. **Persist**: Keep the Cursor plan file as the working brief. If the change names product intent, also patch the Product instance in `_first/FIRST.md` (Basilic: `_first/basilic/PRODUCT.md`) in the same implementation PR. Do not put the plan only in `__dev/` or chat. Backlog items go to GitHub Issues, not `BACKLOG.md`. -10. **Defer when uncertain**: Ask questions when in doubt; defer to user for ambiguous, high-risk decisions diff --git a/.agents/skills/workflow/roadmap/SKILL.md b/.agents/skills/workflow/roadmap/SKILL.md deleted file mode 100644 index 80dc01d4..00000000 --- a/.agents/skills/workflow/roadmap/SKILL.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -name: roadmap -description: Analyze codebase and generate visual roadmap of potential features and improvements. Use when the user types /roadmap. -disable-model-invocation: true ---- - -Analyze codebase and generate a visual roadmap of potential features and improvements. Track progress with todos. - -Canonical horizon: `## Roadmap` in `_first/basilic/PRODUCT.md`. Backlog = GitHub Issues. `__dev/` is scratch. - -1. **Scan codebase**: Scan architecture, patterns, conventions; look for opportunities (missing patterns, performance, DX, user-facing features, refactors) -2. **Identify opportunities**: Feature gaps and improvements. Separate R0 / R-demo / later / not-now using the existing roadmap if present -3. **Create timeline diagram**: Phased overview (Quick Wins, Medium Effort, Strategic) -4. **Create current vs proposed flowchart**: Solid borders for existing, dashed for proposed -5. **Ask user**: Ask whether to update `_first/basilic/PRODUCT.md` (`## Roadmap`) and/or open GitHub Issues. Plans follow @.cursor/rules/base/general.mdc (References, assumptions, deferrals) -6. **Output**: Show the assessment in chat. **Write** `_first/basilic/PRODUCT.md` (`## Roadmap`) when the user confirms a durable horizon change. Do not leave the roadmap only in chat. Do not write `ROADMAP.md` at repo root. Do not treat `__dev/` as the roadmap. diff --git a/.cursor/README.md b/.cursor/README.md index dfd1b210..6dd04315 100644 --- a/.cursor/README.md +++ b/.cursor/README.md @@ -5,10 +5,10 @@ Rules, skills, and MCP for AI-assisted development. Daily workflow: [AI Developm ## Layout - [`rules/`](rules/) — constraints. Glob-scoped except `base/general.mdc`, `base/naming.mdc`, `base/git.mdc`, `base/file-organization.mdc` (always on). -- [`.agents/skills/`](../.agents/skills/) — on-demand expertise (`-v/`) and slash playbooks under `workflow/`. Refresh: `pnpm dlx skills@latest add blockmatic/basilic-skills` ([Cursor Skills](../apps/docu/content/docs/development/cursor-skills.mdx)). Catalog: [blockmatic/basilic-skills](https://github.com/blockmatic/basilic-skills). There is no `.cursor/skills/` tree in this repo. +- [`.agents/skills/`](../.agents/skills/) — on-demand expertise (`-v/`) and slash playbooks under `b/`. Refresh: `pnpm dlx skills@latest add blockmatic/basilic-skills` ([Cursor Skills](../apps/docu/content/docs/development/cursor-skills.mdx)). Catalog: [blockmatic/basilic-skills](https://github.com/blockmatic/basilic-skills). There is no `.cursor/skills/` tree in this repo. - [`mcp.json`](mcp.json) — MCP servers. Setup: [Cursor Setup](../apps/docu/content/docs/development/cursor-setup.mdx). -Type `/` in chat for playbooks (`/plan-feature`, `/git-create-pr`, `/git-commit`, `/retro`). Tech skills load when relevant, or `@.agents/skills/`. +Type `/` in chat for playbooks (`/b-plan-feature`, `/b-git-create-pr`, `/b-git-commit`, `/b-retro`). Tech skills load when relevant, or `@.agents/skills/`. ## Related diff --git a/.cursor/rules/base/general.mdc b/.cursor/rules/base/general.mdc index f0dbaaa8..ca2e1ad2 100644 --- a/.cursor/rules/base/general.mdc +++ b/.cursor/rules/base/general.mdc @@ -4,7 +4,7 @@ alwaysApply: true --- ## Agent Behavior (CRITICAL) -- Docs: technical MDX in `apps/docu/content/docs/`. Product intent: `_first/basilic/PRODUCT.md` (path from `_first/FIRST.md`). **Read** the matching file. Do not `@`-attach docs from rules or skills. Slash playbooks: `.agents/skills/workflow/` (`/name`) +- Docs: technical MDX in `apps/docu/content/docs/`. Product intent: `_first/basilic/PRODUCT.md` (path from `_first/FIRST.md`). **Read** the matching file. Do not `@`-attach docs from rules or skills. Slash playbooks: `.agents/skills/b/` (`/b-`) - After features/fixes: same change, update that MDX and nearest README if behavior, commands, or conventions changed; patch `_first/basilic/PRODUCT.md` if goals, feature map, or horizons changed; glob `.mdc` only if a constraint changed - When creating plans add a ## References section listing rules, skills, and MDX pages used - Summarize assumptions in 3–5 bullets diff --git a/.cursor/rules/base/git.mdc b/.cursor/rules/base/git.mdc index 05c378ab..e69b9380 100644 --- a/.cursor/rules/base/git.mdc +++ b/.cursor/rules/base/git.mdc @@ -7,4 +7,4 @@ alwaysApply: true - Never `--no-verify` or `--trailer` (e.g. Co-authored-by) - Fix lint, type-check, and test failures before committing - Conventional Commits: `(): ` — type/scope lowercase, summary imperative, ≤60 chars, no period. Scope: app (`next`, `fastify`, `docu`), package (`ui`, `core`, `utils`), or omit -- Branch/validate/commit/push/PR: `/exec-push` (@.agents/skills/workflow/exec-push/SKILL.md). Commit message: `/git-commit` +- Branch/validate/commit/push/PR: `/b-exec-push` (read `.agents/skills/b/b-exec-push/SKILL.md`). Commit message: `/b-git-commit` diff --git a/.cursor/rules/base/github-actions.mdc b/.cursor/rules/base/github-actions.mdc index 157a6707..7bd0e08b 100644 --- a/.cursor/rules/base/github-actions.mdc +++ b/.cursor/rules/base/github-actions.mdc @@ -6,4 +6,4 @@ alwaysApply: true - Inspect PR checks, workflow runs, logs, artifacts, and reruns with **`gh`** (authenticated for the repo remote) - Common commands: `gh pr checks`, `gh run list --branch "$(git branch --show-current)"`, `gh run view --log-failed`, `gh run watch `, `gh run download ` - Never use GitHub MCP for Actions — logs, artifacts, and reruns belong to the CLI -- `/fix-github-actions` follows this rule; do not add `gh run watch` to `/git-push` or `/exec-push` +- `/b-fix-github-actions` follows this rule; do not add `gh run watch` to `/b-git-push` or `/b-exec-push` diff --git a/.cursor/rules/cursor/skills.mdc b/.cursor/rules/cursor/skills.mdc index c5365308..694d0656 100644 --- a/.cursor/rules/cursor/skills.mdc +++ b/.cursor/rules/cursor/skills.mdc @@ -9,7 +9,7 @@ Skills are on-demand expertise. `name` must match the folder that contains `SKIL ## Two kinds - **Tech** (`-v/`, e.g. `fastify-v5`): agent-invocable. Required sections: Scope, Assumptions, Principles, Constraints, Interactions -- **Workflow** (`.agents/skills/workflow//`): slash-only. Set `disable-model-invocation: true`. One-line `description`. Not versioned +- **Workflow** (`.agents/skills/b/b-/`): slash-only. Set `disable-model-invocation: true`. One-line `description`. Not versioned. Dispatcher: `.agents/skills/b/SKILL.md` (`/b`) ## Principles - One primary topic per skill; independent versions = separate skills @@ -24,3 +24,5 @@ Skills are on-demand expertise. `name` must match the folder that contains `SKIL Rules (constraints) > skills (patterns) > model Update procedure and inventory: `apps/docu/content/docs/development/cursor-skills.mdx`. Canonical catalog: `blockmatic/basilic-skills`. + +Workflow authoring: read `.agents/skills/b/references/authoring.md`. Shared references stay inside the installable `b` tree. Use explicit inputs, task-specific steps, observable verification, and a handoff; keep simple playbooks short. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..fc8d2d82 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,13 @@ +## Summary + + + +## Release + + + +## Test plan + +- + + diff --git a/.github/workflows/api-e2e.yml b/.github/workflows/api-e2e.yml index 88a2661f..7e02b6c1 100644 --- a/.github/workflows/api-e2e.yml +++ b/.github/workflows/api-e2e.yml @@ -7,8 +7,14 @@ on: - 'packages/email/**' - 'packages/error/**' - 'packages/utils/**' - - 'tools/eslint-config/**' - - 'tools/typescript-config/**' + - 'tools/eslint/**' + - 'tools/typescript/**' + - 'packages/core/**' + - 'packages/cli/**' + - 'apps/api/openapi/**' + - '.github/workflows/api-e2e.yml' + - '.github/actions/setup-pnpm/**' + - '.github/actions/setup-playwright/**' - 'pnpm-lock.yaml' - 'package.json' - 'turbo.json' diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml new file mode 100644 index 00000000..f218a952 --- /dev/null +++ b/.github/workflows/pr-title.yml @@ -0,0 +1,33 @@ +name: PR title + +on: + pull_request: + types: [opened, edited, synchronize, reopened] + +concurrency: + group: pr-title-${{ github.head_ref }} + cancel-in-progress: true + +jobs: + conventional: + name: Conventional PR title + runs-on: ubuntu-latest + permissions: + pull-requests: read + steps: + - uses: amannn/action-semantic-pull-request@0723387faaf9b38adef4775cd42cfd5155ed6017 # v5.5.3 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + types: | + feat + fix + perf + refactor + docs + test + ci + chore + style + revert + requireScope: false diff --git a/.github/workflows/publish-create-basilic.yml b/.github/workflows/publish-create-basilic.yml new file mode 100644 index 00000000..65166c68 --- /dev/null +++ b/.github/workflows/publish-create-basilic.yml @@ -0,0 +1,124 @@ +name: Publish create-basilic + +on: + push: + tags: ['v*'] + +concurrency: + group: publish-create-basilic + cancel-in-progress: false + +jobs: + pack: + name: Assemble, pack, and test tarball + runs-on: ubuntu-latest + timeout-minutes: 60 + outputs: + tarball: ${{ steps.pack.outputs.tarball }} + version: ${{ steps.pack.outputs.version }} + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + ref: ${{ github.sha }} + - uses: ./.github/actions/setup-pnpm + - name: Assemble and build + run: | + CREATE_BASILIC_LOCKFILE=1 pnpm --filter create-basilic assemble + pnpm --filter create-basilic build + - name: Pack once + id: pack + working-directory: tools/create-basilic + run: | + tarball=$(npm pack --pack-destination .) + echo "tarball=$tarball" >> "$GITHUB_OUTPUT" + version=$(node -p "require('./package.json').version") + echo "version=$version" >> "$GITHUB_OUTPUT" + shasum -a 256 "$tarball" | tee "${tarball}.sha256" + - name: Test tarball outside the workspace + env: + TARBALL: ${{ steps.pack.outputs.tarball }} + run: | + work=$(mktemp -d) + tar -xzf "tools/create-basilic/$TARBALL" -C "$work" + dest=$(mktemp -d)/from-tarball + node "$work/package/dist/cli.js" "$dest" --yes + test -f "$dest/package.json" + test ! -d "$dest/apps/docu" + test ! -d "$dest/tools/create-basilic" + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: create-basilic-tarball + path: | + tools/create-basilic/${{ steps.pack.outputs.tarball }} + tools/create-basilic/${{ steps.pack.outputs.tarball }}.sha256 + retention-days: 30 + + publish: + name: Publish npm and GitHub asset + needs: pack + runs-on: ubuntu-latest + environment: release + permissions: + id-token: write + contents: write + steps: + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: create-basilic-tarball + path: dist-tarball + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '24' + registry-url: https://registry.npmjs.org + - name: Mark GitHub Release pending + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + run: | + if ! gh release view "$TAG" >/dev/null 2>&1; then + gh release create "$TAG" --prerelease --title "$TAG" --notes "Pending npm publish" + else + gh release edit "$TAG" --prerelease + fi + - name: Publish packed tarball + env: + VERSION: ${{ needs.pack.outputs.version }} + TARBALL: ${{ needs.pack.outputs.tarball }} + run: | + tag=latest + case "$VERSION" in + *-*) tag=next ;; + esac + npm publish "dist-tarball/$TARBALL" --access public --provenance --tag "$tag" + - name: Attach tarball to GitHub Release + env: + GH_TOKEN: ${{ github.token }} + TAG: ${{ github.ref_name }} + TARBALL: ${{ needs.pack.outputs.tarball }} + VERSION: ${{ needs.pack.outputs.version }} + run: | + gh release upload "$TAG" "dist-tarball/$TARBALL" "dist-tarball/${TARBALL}.sha256" --clobber + case "$VERSION" in + *-*) gh release edit "$TAG" --prerelease --latest=false ;; + *) gh release edit "$TAG" --prerelease=false --latest ;; + esac + gh release view "$TAG" --json url --jq .url + - name: Verify npm package + env: + VERSION: ${{ needs.pack.outputs.version }} + run: | + for attempt in 1 2 3 4 5 6; do + if npm view "create-basilic@$VERSION" dist.integrity; then + break + fi + if [ "$attempt" -eq 6 ]; then + echo "npm view create-basilic@$VERSION failed" + exit 1 + fi + sleep 10 + done + work=$(mktemp -d) + npx --yes "create-basilic@$VERSION" "$work/from-npx" --yes + test -f "$work/from-npx/package.json" + test ! -d "$work/from-npx/apps/docu" + test ! -d "$work/from-npx/tools/create-basilic" diff --git a/.github/workflows/release-impact.yml b/.github/workflows/release-impact.yml new file mode 100644 index 00000000..21ecde48 --- /dev/null +++ b/.github/workflows/release-impact.yml @@ -0,0 +1,24 @@ +name: Release impact + +on: + pull_request: + +concurrency: + group: release-impact-${{ github.ref }} + cancel-in-progress: true + +jobs: + payload: + name: Payload vs PR title + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - name: Check payload paths against title + env: + PR_TITLE: ${{ github.event.pull_request.title }} + PR_BODY: ${{ github.event.pull_request.body }} + PR_BASE_SHA: ${{ github.event.pull_request.base.sha }} + PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: node scripts/release-impact.mjs diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 00000000..b2450110 --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,31 @@ +name: Release Please + +on: + push: + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + name: Open or update release PR + if: ${{ vars.RELEASE_PLEASE_APP_ID != '' }} + runs-on: ubuntu-latest + outputs: + release_created: ${{ steps.release.outputs.release_created }} + tag_name: ${{ steps.release.outputs.tag_name }} + sha: ${{ steps.release.outputs.sha }} + steps: + - uses: actions/create-github-app-token@df432ceedc7162793a195dd1713ff69aefc7379e # v2.0.6 + id: app-token + with: + app-id: ${{ vars.RELEASE_PLEASE_APP_ID }} + private-key: ${{ secrets.RELEASE_PLEASE_APP_PRIVATE_KEY }} + - uses: googleapis/release-please-action@c2a5a2bd6a758a0937f1ddb1e8950609867ed15c # v4.3.0 + id: release + with: + token: ${{ steps.app-token.outputs.token }} + config-file: release-please-config.json + manifest-file: .release-please-manifest.json diff --git a/.github/workflows/scaffold-acceptance.yml b/.github/workflows/scaffold-acceptance.yml new file mode 100644 index 00000000..e48bb2d1 --- /dev/null +++ b/.github/workflows/scaffold-acceptance.yml @@ -0,0 +1,69 @@ +name: Scaffold acceptance + +on: + pull_request: + workflow_dispatch: + +concurrency: + group: scaffold-acceptance-${{ github.ref }} + cancel-in-progress: true + +jobs: + acceptance: + name: Generated tree QA + runs-on: ubuntu-latest + timeout-minutes: 90 + env: + NODE_ENV: test + CI: true + PGLITE: true + ALLOW_TEST: true + RATE_LIMIT_MAX: 10000 + WEBAUTHN_RP_NAME: Test App + DATABASE_URL: postgresql://localhost/test + JWT_SECRET: e2e-jwt-secret-min-32-chars-for-tests + NEXT_PUBLIC_API_URL: http://localhost:3001 + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY || 'sk-ant-dummy-for-scaffold' }} + steps: + - name: Skip on feature PRs + if: github.event_name == 'pull_request' && !startsWith(github.head_ref, 'release-please--') + run: echo 'Full generated QA runs on release-please PRs and workflow_dispatch.' + + - if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + uses: ./.github/actions/setup-pnpm + + - name: Assemble template + if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + env: + CREATE_BASILIC_LOCKFILE: '1' + CREATE_BASILIC_TEMPLATE_DIR: ${{ runner.temp }}/basilic-template + run: pnpm --filter create-basilic assemble + + - name: Assert generated tree + if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + run: node scripts/assert-generated-tree.mjs "$RUNNER_TEMP/basilic-template" + + - name: Secret scan assembled tree + if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + run: | + node scripts/setup-gitleaks.mjs + gitleaks detect --source "$RUNNER_TEMP/basilic-template" --no-git --redact --verbose --config .gitleaks.toml + + - name: Git init and install generated workspace + if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + run: | + cd "$RUNNER_TEMP/basilic-template" + git init + git add -A + git -c user.name=basilic-ci -c user.email=ci@basilic.local commit -m 'chore: assembled template' --no-verify + pnpm install --frozen-lockfile + pnpm --filter @repo/api exec playwright install chromium + pnpm --filter @repo/web exec playwright install chromium + + - name: Generated pnpm qa + if: github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please--') + working-directory: ${{ runner.temp }}/basilic-template + run: pnpm qa diff --git a/.github/workflows/scaffold.yml b/.github/workflows/scaffold.yml new file mode 100644 index 00000000..76808ffa --- /dev/null +++ b/.github/workflows/scaffold.yml @@ -0,0 +1,32 @@ +name: Scaffold + +on: + pull_request: + workflow_dispatch: + +concurrency: + group: scaffold-${{ github.ref }} + cancel-in-progress: true + +jobs: + classify: + name: Template classification + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: ./.github/actions/setup-pnpm + - name: Classify every tracked path + run: pnpm --filter create-basilic exec vitest run src/assemble/classify.test.ts + + generator: + name: Generator tests (${{ matrix.os }}) + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: ./.github/actions/setup-pnpm + - name: Unit tests + run: pnpm --filter create-basilic test diff --git a/.release-please-manifest.json b/.release-please-manifest.json new file mode 100644 index 00000000..e18ee077 --- /dev/null +++ b/.release-please-manifest.json @@ -0,0 +1,3 @@ +{ + ".": "0.0.0" +} diff --git a/AGENTS.md b/AGENTS.md index 60185e11..b37897e8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,8 +27,9 @@ and tests instead of relying on memory or assuming that documentation is current ## FIRST decisions Use FIRST when a task invokes `/f-*` or requires a durable product, journey, -design, architecture, data, API, documentation, workflow, pipeline, quality, -security, or operations decision. +architecture, data, API, documentation, workflow, quality, security, or +operations decision. Interface expression is Journeys. Automated delivery is +Workflow. Load FIRST in this order: @@ -87,7 +88,7 @@ without creating conflicting edits or duplicate work. - When behavior, architecture, commands, or conventions change, update the matching MDX page and nearest README in the same work. - Update `_first/basilic/PRODUCT.md` only when product goals, feature status, - non-goals, metrics, or roadmap horizons change. Passing `pnpm qa` is pipeline + non-goals, metrics, or roadmap horizons change. Passing `pnpm qa` is Workflow evidence, not product success. - When creating a plan, include a `## References` section listing the rules, skills, and documentation used. diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..d585b254 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +Versions are proposed by Release Please from conventional commits. The first stable Basilic distribution is **1.0.0** after Product Ready from the published `create-basilic` package. Preview tags use the npm `next` dist-tag, not `latest`. diff --git a/README.md b/README.md index f4606a88..f18c816f 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ Fork-and-run developer starter: typed SDKs, self-hosted auth, a portable architecture, Cursor-first workflow, and a thin web demo. Fastify • OpenAPI • Next.js • Expo scaffold — one stack, multiple clients. -MIT licensed. Start a product with GitHub **Use this template**, or fork to contribute back. First successful use is [Product Ready](https://basilic-docs.vercel.app/docs/testing/product-ready) (`pnpm setup`, `db:start`, `pnpm reset`, `pnpm dev`, `test@test.ai`). After you own the copy: [After fork](https://basilic-docs.vercel.app/docs/development/after-fork). +MIT licensed. **Start a product** with [`npx create-basilic@latest my-app`](tools/create-basilic/README.md). Clone or GitHub **Use this template** still works. **Fork** to contribute back. First successful use is [Product Ready](https://basilic-docs.vercel.app/docs/testing/product-ready) (`pnpm setup`, `db:start`, `pnpm reset`, `pnpm dev`, `test@test.ai`). After you own the copy: [After fork](https://basilic-docs.vercel.app/docs/development/after-fork). ## Features -- 🤖 **AI-first dev workflow** — Agent rules, skills, MCP integrations, and automated CodeRabbit reviews +- 🤖 **AI-first dev workflow** — Agent rules, `/b` workflow skills, FIRST `/f-*` decisions, MCP integrations, and automated CodeRabbit reviews - 🔌 **REST API & JWT** — OpenAPI spec, Swagger UI, JWT and API key auth for all clients - 📦 **SDK generation** — Type-safe clients from OpenAPI via HeyAPI - 🧩 **Web + API starters** — Next.js demo, React hooks, Expo UI scaffold, Fastify API (not a wallet or OpenAI template) diff --git a/_first/ABOUT.md b/_first/ABOUT.md index f42e4d7c..312bf36c 100644 --- a/_first/ABOUT.md +++ b/_first/ABOUT.md @@ -1,17 +1,17 @@ # First Principles -Version: 0.2-draft -Last reviewed: 2026-09-04 +Version: 0.3-draft +Last reviewed: 2026-09-05 Some decisions are too consequential to become afterthoughts. Important project knowledge should live in files, not disappear into conversations. This is an agent-first factory, not an agent-autonomous one. Humans still decide product scope, security-sensitive changes, and anything that cannot be recovered from the repository. Agents inspect, propose, implement, and update durable context. They do not silently invent the goal. -"First" means: make the concern explicit before implementation, chat, or a generated UI invents it. FIRST is a spec-to-code factory: shared language between agents and domain experts, product and system design before code. The twelve are stations on one factory, not a waterfall and not competing religions. +"First" means: make the concern explicit before implementation, chat, or a generated UI invents it. FIRST is a spec-to-code factory: shared language between agents and domain experts, product and system design before code. The ten are stations on one factory, not a waterfall and not competing religions. ## Who this is for -- **Users of the framework** — adopting FIRST in a product repo. Install `npx skills add blockmatic/first`. Copy the user pack into `_first/`. Edit [FIRST.md](FIRST.md) and opted-in station files from [templates/](templates/). Merge a pointer into root `AGENTS.md`. +- **Users of the framework** — adopting FIRST in a product repo. Install `npx skills add blockmatic/first`. Copy the user pack into `_first/`. Edit [FIRST.md](FIRST.md) and write overlays only for stations listed as In. Merge a pointer into root `AGENTS.md`. - **Maintainers of the framework** — evolving FIRST itself. Start at [`blockmatic/first` maintainers](https://github.com/blockmatic/first/blob/main/_first/maintainers/README.md). Do not copy `maintainers/` or `instance/`. ## Dual audience (minimum) @@ -19,13 +19,13 @@ This is an agent-first factory, not an agent-autonomous one. Humans still decide - **User of the framework:** install `/f`, copy the user pack, write `FIRST.md`, keep product facts in instance files or docs those files point at. - **Maintainer of the framework:** edit `principles/`, `articles/`, `skills/f/`, `maintainers/`, and `packages/validate`. Do not encode one adopter’s product into generic files. -Each principle’s **Definition of Done** is that station’s artifact. It is not the whole factory, not CI green, and not Product success after use. Quality / Product / Pipelines qualify “done” differently — see Boundaries below. +Each principle’s **Definition of Done** is that station’s artifact. It is not the whole factory, not CI green, and not Product success after use. Quality / Product / Workflow qualify “done” differently — see Boundaries below. ## Audiences Inside the user pack: -- **Humans reading:** start at [README.md](README.md). Essays live on the FIRST site, not in this pack. +- **Humans reading:** start at [README.md](README.md). Essays in `articles/` are factory-only upstream; adopters use instance overlays and docs their pack points at ([articles on GitHub](https://github.com/blockmatic/first/tree/main/_first/articles) if you want the argument). - **Agents operating:** start at [AGENTS.md](AGENTS.md), then this file, then [FIRST.md](FIRST.md), then the matching `/f-*` skill. - **Humans applying:** this file plus the matching `/f-*` skill (or `principles/X.md` in this factory repo). The essay is optional once you know the argument. @@ -42,10 +42,9 @@ npx skills add blockmatic/first - [AGENTS.md](AGENTS.md) - [ABOUT.md](ABOUT.md) (this file) - [README.md](README.md) if you want the human door -- [templates/](templates/) — copy only stations listed as In - [FIRST.md](FIRST.md) — create this; list in and out -[README.md](README.md) is the human door in this pack. Essays live on the FIRST site. They are not required for an agent to operate. Do not copy `principles/` or `articles/` into an adopter repo. Skip `maintainers/` and `instance/`. Layout and why: [PACKAGING.md](https://github.com/blockmatic/first/blob/main/_first/maintainers/PACKAGING.md). +[README.md](README.md) and `articles/` are the human pack in this factory. They are not required for an agent to operate. Do not copy `principles/` or `articles/` into an adopter repo. Skip `maintainers/` and `instance/`. Layout and why: [PACKAGING.md](https://github.com/blockmatic/first/blob/main/_first/maintainers/PACKAGING.md). ## Documentation inventory @@ -55,16 +54,29 @@ User pack — copy these (plus `FIRST.md` you create): |---|---|---| | [README.md](README.md) | Users, humans | Front door, copy instructions, station index | | [AGENTS.md](AGENTS.md) | Users, agents | Load order, operating rules, reusable prompt | -| [ABOUT.md](ABOUT.md) | Users, both | Canonical map of stations, loops, and boundaries | +| [ABOUT.md](ABOUT.md) | Users, both | Canonical map of stations, loops, boundaries, overlay contract | | [FIRST.md](FIRST.md) | Users, both | This repo’s in/out map and instance paths | -| [templates/](templates/) | Users applying | Empty overlay structs; copy only opted-in stations | -| Installed `/f-*` | Users applying and agents operating | Operational spec from `npx skills add blockmatic/first` | +| Installed `/f-*` | Users applying and agents operating | Operational spec (copy of `principles/X.md` in this factory) | -Factory-only (do not copy into an adopter repo): `articles/X.md` and `principles/X.md` in [blockmatic/first](https://github.com/blockmatic/first). Essays argue; principles operate. Same filenames. +## Overlay contract + +Overlays are deltas, not a second copy of `/f-*`. Create a file only for a station listed as In. Absent files beat empty stubs. Do not generate ten skeletons. + +Required `##` headings: + +`Principle` · `Artifacts` · `Minimum Useful Artifact` · `Notes` + +- **Principle:** `See /f-`. +- **Artifacts:** label **Fact**, **Drift**, and **Unresolved**. Point at canonical docs. Do not paste Recipe, Statement, Outcome, Validation, Definition of Done, or Agent Prompt from the spec. +- Product may add `Brief` · `Feature map` · `Roadmap` after Notes when the overlay is also the canonical brief. + +`FIRST.md` In keys are the ten stations. A Google-format `DESIGN.md` is a Journeys artifact, not a station; point at it under Journeys when the file exists. + +Factory-only (do not copy into an adopter repo): `articles/X.md` and `principles/X.md`. Essays argue; principles operate. Same filenames. Maintainer pack — do not copy: [maintainers/](https://github.com/blockmatic/first/tree/main/_first/maintainers). Skip the factory `instance/` overlays. The source-tree validator is `packages/validate` in [blockmatic/first](https://github.com/blockmatic/first). -The canonical station order is Product → Journeys → Design → Architecture → Data → API → Documentation → Workflow → Pipelines → Quality → Security → Operations. The filenames are identical across `articles/` and `principles/`; the directory names distinguish argument from operation. +The canonical station order is Product → Journeys → Architecture → Data → API → Documentation → Workflow → Quality → Security → Operations. The filenames are identical across `articles/` and `principles/`; the directory names distinguish argument from operation. ## Lifecycle @@ -92,42 +104,38 @@ Inspect before generating. Preserve intentional existing decisions. Distinguish idea → plan → implement → review → pipeline signals → approval → release → learning -Pipelines are the automated stretch of that loop. Do not keep a third loop in this file. +The automated stretch of that loop lives here too. Do not keep a third loop in this file. -## The twelve +## The ten 1. **Product** — what we are building, for whom, why it is worth building, and how we will know -2. **Journeys** — how someone finishes a job, including errors, permissions, and state -3. **Design** — how the product behaves and communicates through its interface -4. **Architecture** — how the system is divided, depends, and deploys before local choices harden into structure -5. **Data** — the canonical domain model, ownership, lifecycle, and evolution before stores proliferate competing truths -6. **API** — the capability and its boundary, before consumers couple to an accident -7. **Documentation** — which context must remain durable and discoverable -8. **Workflow** — how work moves between humans, agents, tools, and decisions -9. **Pipelines** — how a change reaches a validated, deployable state -10. **Quality** — what "good" means before anyone optimizes toward an undefined target -11. **Security** — what we are trusting, protecting, exposing, and allowing -12. **Operations** — how we see, support, and recover the running system - -Event taxonomy is Product. Domain concepts, ownership, retention, and schema evolution are Data. External capability schemas are API. Eval datasets are Quality. Telemetry is Operations. Deployment topology is Architecture; deployment execution is Pipelines. +2. **Journeys** — how someone finishes a job, including errors, permissions, state, and how the interface expresses it +3. **Architecture** — how the system is divided, depends, and deploys before local choices harden into structure +4. **Data** — the canonical domain model, ownership, lifecycle, and evolution before stores proliferate competing truths +5. **API** — the capability and its boundary, before consumers couple to an accident +6. **Documentation** — which context must remain durable and discoverable +7. **Workflow** — how work moves, including how a change reaches a validated, deployable state +8. **Quality** — what "good" means before anyone optimizes toward an undefined target +9. **Security** — what we are trusting, protecting, exposing, and allowing +10. **Operations** — how we see, support, and recover the running system + +Event taxonomy is Product. Domain concepts, ownership, retention, and schema evolution are Data. External capability schemas are API. Eval datasets are Quality. Telemetry is Operations. Deployment topology is Architecture; deployment execution is Workflow. ## Boundaries Each station may point at a sibling in one sentence. It may not re-teach it. - **Product** owns what, why, how we will know, the event taxonomy, and GTM as a field. Not release bars, not error rates, not marketing automation. -- **Journeys** owns what happens: actors, states, where permission gates occur, and completion. Security owns the permission policy. Not pixels. Not GTM acquisition maps. -- **Design** owns how the interface expresses the job. Not the state model. +- **Journeys** owns what happens and how the interface expresses the job: actors, states, where permission gates occur, and completion. Security owns the permission policy. Not GTM acquisition maps. - **Architecture** owns system decomposition, dependency direction, deployment topology, and structural tradeoffs. Not endpoint shape or runtime health. - **Data** owns canonical domain concepts, data ownership, lifecycle, retention, lineage, and schema evolution. Not product event goals or external contract shape. - **API** owns capability and contract shape, including how Security-owned authorization requirements and denials appear at the boundary. Not the authorization policy. - **Documentation** owns durable memory. Owns "chat is not the system of record" once. -- **Workflow** owns actors, handoffs, issue/PR state, and human gates. -- **Pipelines** owns the automated path, the commit-stage build, and readable failures. Not what "good" means. +- **Workflow** owns actors, handoffs, issue/PR state, human gates, the automated path, the commit-stage build, and readable failures. Not what "good" means. - **Quality** owns the release bar: tests, evals, budgets. Not funnels, not "CI ran." - **Security** owns trust, identities, authorization policy, secrets, classification, and the agent as a principal. Not contract shape. - **Operations** owns runtime health, recovery, and verify-in-the-running-system. Not product analytics. -"Done" is always qualified. Quality: the bar was met. Product: observation after use. Pipelines: the bar actually ran. +"Done" is always qualified. Quality: the bar was met. Product: observation after use. Workflow: the bar actually ran. Human gates — product scope, security-sensitive changes, destructive operations — are Workflow and Security. The other stations point at them. They do not reprint them. diff --git a/_first/FIRST.md b/_first/FIRST.md index 9006439e..74d33cea 100644 --- a/_first/FIRST.md +++ b/_first/FIRST.md @@ -1,6 +1,6 @@ # FIRST -spec: 0.2-draft +spec: 0.3-draft This repository’s instance map. Other users of the framework should list only the stations they opted into. Absent files beat empty stubs. Do not copy `basilic/` out. @@ -9,18 +9,19 @@ Durable product facts live in [basilic/PRODUCT.md](basilic/PRODUCT.md). That ove ## In - product: [basilic/PRODUCT.md](basilic/PRODUCT.md) -- journeys: [basilic/JOURNEYS.md](basilic/JOURNEYS.md) -- design: [basilic/DESIGN.md](basilic/DESIGN.md) — no Google-format `DESIGN.md` yet; tokens in `packages/ui`. When added, use [DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md) +- journeys: [basilic/JOURNEYS.md](basilic/JOURNEYS.md) — no Google-format `DESIGN.md` yet; tokens in `packages/ui`. When added, it is a Journeys artifact ([DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md)), not a station - architecture: [basilic/ARCHITECTURE.md](basilic/ARCHITECTURE.md) - data: [basilic/DATA.md](basilic/DATA.md) - api: [basilic/API.md](basilic/API.md) - documentation: [basilic/DOCUMENTATION.md](basilic/DOCUMENTATION.md) - workflow: [basilic/WORKFLOW.md](basilic/WORKFLOW.md) -- pipelines: [basilic/PIPELINES.md](basilic/PIPELINES.md) - quality: [basilic/QUALITY.md](basilic/QUALITY.md) - security: [basilic/SECURITY.md](basilic/SECURITY.md) - operations: [basilic/OPERATIONS.md](basilic/OPERATIONS.md) +- pipelines: [basilic/PIPELINES.md](basilic/PIPELINES.md) + +Optional `_first/DESIGN.md` is not a station and is not written yet. Tokens live in `packages/ui`. When added, use [DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md). ## Out -None for this adopter. A typical adopter omits stations they do not instantiate (for example Design with no UI, Operations before production). +None for this adopter. A typical adopter omits stations they do not instantiate (for example Operations before production). diff --git a/_first/README.md b/_first/README.md index 22815446..1fd03f9a 100644 --- a/_first/README.md +++ b/_first/README.md @@ -4,11 +4,11 @@ Basilic is an **adopter** of FIRST. The factory source is [`blockmatic/first`](h This folder holds: -- Vendored user pack: [`README.md`](README.md), [`AGENTS.md`](AGENTS.md), [`ABOUT.md`](ABOUT.md), [`templates/`](templates/) +- Vendored user pack: [`README.md`](README.md), [`AGENTS.md`](AGENTS.md), [`ABOUT.md`](ABOUT.md) - This repository’s instance: [`FIRST.md`](FIRST.md) and [`basilic/`](basilic/) - Installed station skills: `.agents/skills/f/` via `npx skills add blockmatic/first` -Do not edit the vendored files to encode Basilic facts. Put those in `FIRST.md` and `basilic/`. When updating from upstream, replace `README.md`, `AGENTS.md`, `ABOUT.md`, and `templates/` after reviewing the diff. Refresh `/f` with the skills CLI. Never overwrite `FIRST.md` or `basilic/`. +Do not edit the vendored files to encode Basilic facts. Put those in `FIRST.md` and `basilic/`. When updating from upstream, replace `README.md`, `AGENTS.md`, and `ABOUT.md` after reviewing the diff. Refresh `/f` with the skills CLI. Never overwrite `FIRST.md` or `basilic/`. Do not copy `templates/` — overlays exist only for stations listed as In. ## Load @@ -20,7 +20,7 @@ Root [`AGENTS.md`](../AGENTS.md) points to [`AGENTS.md`](AGENTS.md). Canonical l npx skills add blockmatic/first ``` -Then replace `_first/README.md`, `_first/AGENTS.md`, `_first/ABOUT.md`, and `_first/templates/` from a first release after reviewing the diff. Skip `instance/`, `maintainers/`, `principles/`, and `articles/`. Essays live on [GitHub](https://github.com/blockmatic/first/tree/main/_first/articles). +Then replace `_first/README.md`, `_first/AGENTS.md`, and `_first/ABOUT.md` from a first release after reviewing the diff. Skip `instance/`, `maintainers/`, `principles/`, and `articles/`. Essays live on [GitHub](https://github.com/blockmatic/first/tree/main/_first/articles). ## Human door diff --git a/_first/basilic/API.md b/_first/basilic/API.md index 7056e2bf..d7164d2f 100644 --- a/_first/basilic/API.md +++ b/_first/basilic/API.md @@ -2,15 +2,7 @@ ## Principle -Define the capability and its boundary before consumers couple to an accidental implementation. - -## Statement - -I treat the capability and its boundary as a design decision, not an implementation leftover. Before a second consumer depends on a shape, I want to know what goes in, what comes out, what fails, which Security-owned authorization requirement applies, and how denial appears. That contract might be HTTP, a typed module, an event, a CLI, or an agent tool. The format matters less than making the boundary explicit on purpose. - -## Outcome - -Meaningful boundaries have explicit inputs, outputs, errors, and references to applicable authorization requirements. Implementations enforce those requirements and conform, or the contract is updated deliberately. Agent tools have the same explicitness as HTTP routes. A second consumer can be written without reading the handler. +See /f-api. ## Artifacts @@ -23,7 +15,6 @@ Meaningful boundaries have explicit inputs, outputs, errors, and references to a - **Fact:** `createClient` modes: no-auth, JWT (`getAuthToken` / refresh), apiKey (`bask_…`) - **Fact:** Drift check: `pnpm generate && git diff --exit-code -- apps/api/openapi/openapi.json packages/core/src/gen` (also `api-e2e.yml`) - **Fact:** Generation how-to: [openapi-generation.mdx](../../apps/docu/content/docs/development/openapi-generation.mdx) -- **Fact:** Architecture index matches this overlay: TypeBox on Fastify routes generate OpenAPI. ADR 009: Fastify routes are the source. - **Unresolved:** public versioning/idempotency policy beyond regenerate-on-change; MCP server as a shipped app; GraphQL secondary interface (ADR 009 optional) Who may call is Security. This station owns how credentials and denial appear on the contract. @@ -37,50 +28,8 @@ Who may call is Security. This station owns how credentials and denial appear on - versioning: do not hand-edit OpenAPI; breaking change is a TypeBox change plus generate - contract check: generate + `git diff --exit-code` -## Recipe - -1. Inspect `apps/api/src/routes/`, TypeBox, OpenAPI, `@repo/core`, `@repo/react`, CLI, error catalog, tests. -2. Understand capabilities and consumers (web, CLI, not mobile). -3. Identify undocumented boundaries, spec/implementation drift, inconsistent errors. -4. Propose the smallest useful contract change. Keep intentional existing decisions. -5. Make inputs, outputs, errors, and denial explicit in TypeBox. Point at Security for who may invoke. -6. Implement against the contract. Do not let the handler invent a parallel shape. Do not edit `openapi.json`. -7. Validate with generate + drift check and API Vitest (`fastify.inject()`). -8. Update generated OpenAPI and `@repo/core` in the same work; handwritten `@repo/react` hooks when the UI needs them. - -## Validation - -- A second consumer can be written against OpenAPI / `@repo/core` without reading the handler. -- Request and response shapes match TypeBox → OpenAPI, or the discrepancy is named. -- Errors follow the catalog, not a dialect per endpoint. -- Authorization is referenced and enforced at Fastify, not redefined as comments. Policy lives in Security. -- CLI has name, inputs, outputs, and failure behavior (`packages/cli`). -- Breaking changes are visible before merge (drift job). - -## Definition of Done - -Contracts are documented (TypeBox + generated OpenAPI) and implementations conform, or discrepancies are named. Breaking changes are identified before merge. - -## Agent Prompt - -Apply API First to Basilic. - -Read architecture/api MDX, ADR 009, OpenAPI generation docs, TypeBox routes, `apps/api/openapi/openapi.json`, `@repo/core`, `@repo/react`, error catalog, CLI, and tests. Do not assume a hand-edited spec is correct. - -Source of truth is TypeBox on the route. OpenAPI and `@repo/core` are generated. Do not define a new authorization policy here. Do not widen into a public platform or GraphQL unless the product already is that. - -Propose the smallest useful boundary: one operation made explicit in TypeBox. Implement against the contract. Validate with generate + drift check and API tests. When behavior changes, regenerate OpenAPI and `@repo/core` in the same work. Update this instance when paths or checks change. - ## Notes -**API vs Product:** Product names the capability and why it exists. API names how systems ask for it. - -**API vs Architecture:** Architecture decides which parts communicate. API defines the contract across that boundary. - -**API vs Data:** Data owns canonical domain meaning. API owns the consumer-facing representation. - -**API vs Security:** Security owns who may invoke a capability. API owns how that requirement, credentials, and denial appear at the boundary. - -**API vs Documentation:** TypeBox/OpenAPI is the contract. MDX explains why it looks that way. +Product names the capability. Architecture decides which parts communicate. Data owns canonical domain meaning. Security owns who may invoke. Do not edit `openapi.json`. Do not widen into a public platform or GraphQL unless the product already is that. -**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/API.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/API.md) · [Factory map](../ABOUT.md) · [API architecture](../../apps/docu/content/docs/architecture/api.mdx) · [OpenAPI generation](../../apps/docu/content/docs/development/openapi-generation.mdx) +**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/API.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/API.md) · [Factory map](../ABOUT.md) · [API](../../apps/docu/content/docs/architecture/api.mdx) · [ADR 009](../../apps/docu/content/docs/adrs/009-api-architecture.mdx) diff --git a/_first/basilic/ARCHITECTURE.md b/_first/basilic/ARCHITECTURE.md index 7c7c47b6..1304c806 100644 --- a/_first/basilic/ARCHITECTURE.md +++ b/_first/basilic/ARCHITECTURE.md @@ -2,22 +2,14 @@ ## Principle -Decide system boundaries, dependency direction, and deployment shape before local implementation choices harden into structural constraints. - -## Statement - -I treat architecture as the small set of decisions that are expensive to reverse: where responsibilities live, which dependencies point where, what crosses a process or trust boundary, and how the system is deployed. I do not design every class in advance. I make the consequential structure visible before the codebase makes it accidentally. - -## Outcome - -The system has an inspectable structural model at the level its scale requires. Components have named responsibilities and owners. Dependency direction, external systems, data stores, and deployment units are visible. Consequential choices have rationale and known tradeoffs. Implementation conforms, or the model is updated deliberately. +See /f-architecture. ## Artifacts - **Fact:** [`../../apps/docu/content/docs/architecture/index.mdx`](../../apps/docu/content/docs/architecture/index.mdx) — stack overview - **Fact:** [`../../apps/docu/content/docs/architecture/monorepo.mdx`](../../apps/docu/content/docs/architecture/monorepo.mdx) — apps vs packages -- **Fact:** [`../../apps/docu/content/docs/adrs/`](../../apps/docu/content/docs/adrs/) — ADRs 001–011 -- **Fact:** Deployables: `apps/api` (system of record), `apps/web`, `apps/mobile` (UI scaffold), `apps/docu` +- **Fact:** [`../../apps/docu/content/docs/adrs/`](../../apps/docu/content/docs/adrs/) — ADRs 001–012 +- **Fact:** Deployables: `apps/api` (system of record), `apps/web`, `apps/mobile` (UI scaffold), `apps/docu`. Generator: `tools/create-basilic` (not a generated-app deployable). - **Fact:** Packages: `core` (generated client), handwritten `react`, `ui`, `utils`, `error`, `cli`, `email`. Security mail is `@repo/email` + Fastify `emailProvider`. - **Fact:** Apps depend on packages, never the reverse. `react` depends on `core`. Clients call Fastify over HTTP. - **Fact:** Store: PostgreSQL via `DATABASE_URL`. PGLite when `PGLITE=true` **or** `NODE_ENV=test`. Compiled PGLite requires SQL copied into `dist`. Supabase is the managed host, not the auth SDK. @@ -27,8 +19,11 @@ The system has an inspectable structural model at the level its scale requires. - **Fact:** Web auth hop: browser → Next cookie route → SDK → Fastify. Cookie is SSR/security copy. Fastify is session SoT and revocation. Refresh reuse-grace on previous `jti`. - **Fact:** Next `proxy.ts` is a JWT UI gate (shared `JWT_SECRET`), not revocation. - **Fact:** `/health` is readiness: 503 when DB probe fails; no deep third-party probes +- **Drift:** concurrent legitimate refresh can revoke the live session (web proxy + SDK + Fastify rotate) +- **Drift:** compiled PGLite startup can miss migration SQL under `dist` - **Unresolved:** GCP/AWS as first-class deploy targets; mobile as an API consumer - **Unresolved:** dedicated ADRs for custom JWT + cookie BFF (rationale lives in authentication MDX) +- **Unresolved:** refresh ownership / concurrency protocol (Security shares this) ```mermaid flowchart LR @@ -53,51 +48,10 @@ flowchart LR - deployable units: Fastify API, Next.js web, Expo mobile, Fumadocs - data store: PostgreSQL (Drizzle in `apps/api`) - dependencies: clients → `@repo/core` → HTTP → Fastify/TypeBox → Drizzle -- consequential decisions: ADRs 001 (monorepo), 002 (Fastify), 003 (Next), 004 (shadcn), 007/008 (Drizzle/Postgres), 009 (routes generate OpenAPI) - -## Recipe - -1. Inspect `apps/*`, `packages/*`, architecture MDX, ADRs, [`../../apps/docu/content/docs/deployment/`](../../apps/docu/content/docs/deployment/), Drizzle, and env. -2. Understand product capabilities and quality attributes that constrain the structure. -3. Identify accidental coupling (app-to-app imports, hand-edited OpenAPI, mobile pretending to be a client). -4. Propose the smallest structural clarification or ADR. Prefer one boundary over a redesign. -5. Record rationale, alternatives, consequences, and reconsideration trigger when consequential. -6. Implement with apps → packages direction visible. -7. Validate against code and deployment, not only the directory tree. -8. Update architecture MDX and ADRs when structure changes; update this instance with paths. - -## Validation - -- A new contributor can name apps, packages, responsibilities, and dependency direction from architecture MDX. -- Diagrams match code and deployment, or discrepancies are named (mobile, PostHog). -- New dependencies follow apps → packages; generated client from OpenAPI; no app-to-app imports. -- Consequential choices have ADRs, not only a technology name. -- Architecture stays proportional to a starter toolkit. - -## Definition of Done - -The structural decision is explicit, implemented, and validated. Boundaries and dependency direction are inspectable. Durable architecture artifacts match the system or record deliberate drift. - -## Agent Prompt - -Apply Architecture First to Basilic. - -Read `apps/docu/content/docs/architecture/`, ADRs 001–011, Turborepo layout, deployment MDX, Drizzle, and external integrations. Inspect implementation; do not assume diagrams are current. - -Treat Fastify route TypeBox as the HTTP contract source. OpenAPI is generated. Preserve intentional existing structure. Do not introduce services, layers, queues, or platforms because they are common elsewhere. Do not wire `apps/mobile` to `@repo/core` without a product decision. - -Propose the smallest useful structural change or ADR. Record rationale and tradeoffs. Validate against code and deployment. Update architecture MDX when structure changes. Update this instance when paths change. +- consequential decisions: ADRs 001 (monorepo), 002 (Fastify), 003 (Next), 004 (shadcn), 007/008 (Drizzle/Postgres), 009 (routes generate OpenAPI), 012 (create-basilic + Release Please) ## Notes -**Architecture vs Product:** Product names the capability and constraints. Architecture assigns responsibilities and structural boundaries. - -**Architecture vs Data:** Architecture places stores and data flows. Data owns canonical meaning, ownership, lifecycle, and evolution. - -**Architecture vs API:** Architecture decides which components communicate. API defines the contract across a boundary (TypeBox, then generated OpenAPI). - -**Architecture vs Pipelines:** Architecture describes deployment units and topology. Pipelines build, validate, and deliver them. - -**Architecture vs Operations:** Architecture names the running parts. Operations observes and recovers them. +Product names the capability. Architecture assigns responsibilities. Data owns canonical meaning. API defines the contract. Workflow builds and delivers deployables. Operations observes them. Do not introduce services, layers, or queues because they are common elsewhere. Do not wire `apps/mobile` to `@repo/core` without a product decision. **Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/ARCHITECTURE.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/ARCHITECTURE.md) · [Factory map](../ABOUT.md) · [Monorepo](../../apps/docu/content/docs/architecture/monorepo.mdx) · [API](../../apps/docu/content/docs/architecture/api.mdx) diff --git a/_first/basilic/DATA.md b/_first/basilic/DATA.md index 6fe01d22..77300164 100644 --- a/_first/basilic/DATA.md +++ b/_first/basilic/DATA.md @@ -2,15 +2,7 @@ ## Principle -Define canonical domain concepts, ownership, lifecycle, and change rules before stores, schemas, and events proliferate competing truths. - -## Statement - -I treat data as durable product state with meaning, ownership, and a lifecycle—not as columns left behind by features. Before several stores or consumers encode the same concept differently, I want to know what the concept means, which system is authoritative, how identity works, how it changes, how long it lives, and how it is removed. - -## Outcome - -Core domain concepts have shared names and definitions. Each important dataset has an owner and authoritative source. Identity, constraints, retention, deletion, lineage, and schema evolution are explicit at the level the product needs. Migrations preserve or deliberately transform meaning. Copies do not silently become competing sources of truth. +See /f-analyst. ## Artifacts @@ -37,49 +29,8 @@ For `users` (and the same shape for any concept you touch): - retention/deletion: **unresolved** - evolution: Drizzle migrations under `apps/api/src/db/migrations/` -## Recipe - -1. Inspect `apps/api/src/db/schema/`, migrations, auth/account handlers, analytics docs, and caches. -2. Understand where each concept is created, changed, copied, and deleted. -3. Identify competing definitions, unclear ownership, or extra tables invented from prose. -4. Propose the smallest useful clarification—one concept, constraint, or migration rule. -5. Define canonical meaning in Drizzle before adding a store or OpenAPI-only field. -6. Implement with `drizzle-kit generate`. Do not edit generated SQL as a parallel schema. -7. Validate read/write paths and deletion/unlink behavior (last-method guardrail). -8. Update ADRs or schema comments when meaning, ownership, or lifecycle changes; update this instance. - -## Validation - -- A contributor can name PostgreSQL via Drizzle as authoritative for account and session data. -- Core concepts have one meaning in tables and TypeBox representations. -- Domain invariants are enforced in DB constraints and TypeBox, not comments alone. -- Schema changes include a generated migration and rollback/recovery reasoning. -- Retention and deletion match Security and product requirements, or are marked unresolved. - -## Definition of Done - -The affected domain concepts, ownership, invariants, lifecycle, and evolution rules are explicit. Implementations and migrations preserve them, or deliberate transformations and unresolved risks are documented. - -## Agent Prompt - -Apply Data First to Basilic. - -Read ADR 007/008, `apps/api/src/db/schema/`, migrations, and handlers that read and write them. Trace data from creation through copies and deletion. Do not assume the schema is the whole domain model. Do not invent tables for `oauth_state` — they are verification types. - -Preserve intentional existing models. Do not introduce a warehouse, event bus, or new source of truth. Propose the smallest useful data decision. Implement with Drizzle generate. Validate real read/write paths where safe. Update durable artifacts when meaning, ownership, lifecycle, or schema changes. Update this instance when paths change. - ## Notes -**Data vs Product:** Product owns event taxonomy and outcomes to measure. Data owns meaning, authority, and lifecycle of records. - -**Data vs Architecture:** Architecture places stores and data flows. Data defines what the state means and which source is authoritative. - -**Data vs API:** Data owns the canonical domain model. API owns the consumer-facing representation. - -**Data vs Security:** Data records classification, retention, and deletion requirements. Security owns access policy. - -**Data vs Quality:** Data owns domain invariants. Quality owns release gates and eval datasets. - -**Data vs Operations:** Data owns product state. Operations owns telemetry about runtime health. +Product owns event taxonomy. Architecture places stores. Data owns meaning and authority. API owns the consumer-facing representation. Do not invent tables for `oauth_state` — they are verification types. Do not introduce a warehouse or event bus. -**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/DATA.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/DATA.md) · [Factory map](../ABOUT.md) · [ADR 008](../../apps/docu/content/docs/adrs/008-database.mdx) +**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/DATA.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/DATA.md) · [Factory map](../ABOUT.md) · [ADR 007](../../apps/docu/content/docs/adrs/007-backend-orm.mdx) · [ADR 008](../../apps/docu/content/docs/adrs/008-database.mdx) diff --git a/_first/basilic/DESIGN.md b/_first/basilic/DESIGN.md deleted file mode 100644 index 88e3875e..00000000 --- a/_first/basilic/DESIGN.md +++ /dev/null @@ -1,70 +0,0 @@ -# Design First - -## Principle - -Decide how the product behaves and communicates through its interface before layout and components become accidental consequences of implementation. - -## Statement - -Useful software is the floor. The product people remember is the one that feels clear, thoughtful, and enjoyable to use. I treat design as behavior, hierarchy, states, copy, and motion — not decoration bolted on at the end. Before I add a pattern, I look at what the project already has and reuse it. Invention is for when the problem actually needs it. - -## Outcome - -The interface follows a coherent design system: tokens, primitives, composition rules, and content patterns. Loading, empty, error, success, and disabled states are defined. New UI reuses established patterns. The agent-readable identity and the implementation agree, or the discrepancy is named. Browser verification across states is the bar. - -## Artifacts - -- **Fact:** Demo shell brand is **Basilic** (sidebar). Markets home uses `@repo/ui` + `tokens.css` (`text-chart-2` / `text-destructive` for 24h change). No second palette. -- **Fact:** No Google-format `_first/DESIGN.md` yet. Do not invent a second palette to complete the template. When added, use [DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md) at `_first/DESIGN.md` (or one path listed in [../FIRST.md](../FIRST.md)). -- **Fact:** Tokens: [`../../packages/ui/src/styles/tokens.css`](../../packages/ui/src/styles/tokens.css) — semantic colors, sidebar, radius, `@theme inline`, Inter / Poppins / mono -- **Fact:** Components: `@repo/ui` (shadcn/ui, Radix, Tailwind 4). ADR [004](../../apps/docu/content/docs/adrs/004-design-system.mdx). Frontend: [frontend.mdx](../../apps/docu/content/docs/architecture/frontend.mdx) -- **Fact:** Apps consume `@repo/ui`; app-only UI collocated in `apps/web` / `apps/mobile` / `apps/docu` -- **Fact:** Skills: `shadcn-v3`, `tailwind-design-system-v4`, `frontend-design-v1`; playbook `/audit-accessibility`, `/use-shadcn` -- **Unresolved:** Google-format `_first/DESIGN.md`; motion guidelines; copy patterns beyond component defaults - -## Minimum Useful Artifact - -- user goal and states: per feature (default, loading, empty, error, success, disabled) -- reuse: `@repo/ui` + `tokens.css` -- interaction: follow adjacent screens; do not introduce hex or typefaces outside tokens -- a11y: keyboard, contrast, focus, `prefers-reduced-motion` via `/audit-accessibility` -- verification: browser across states — not a single default screenshot - -## Recipe - -1. Inspect ADR 004, frontend MDX, `packages/ui`, `tokens.css`, and adjacent screens. There is no Google-format `DESIGN.md`. -2. Understand required states for the feature. -3. Identify missing states or values outside the system. -4. Propose primitives from `@repo/ui`. New components only when nothing fits (`/use-shadcn`). -5. Define click, submit, dismiss, back. Motion only for feedback. -6. Implement with existing primitives. No parallel palette. -7. Validate in the browser across states, keyboard, and reduced motion. -8. Update `@repo/ui` or MDX if you introduced a reusable pattern; update this instance. - -## Validation - -- New UI uses existing tokens and `@repo/ui` unless a gap is documented. -- Tokens in CSS and usage agree. No second palette. Absent Google-format `DESIGN.md` is named, not silently filled. -- Required states render and behave. -- Motion respects `prefers-reduced-motion`. -- A screenshot of the default view is not verification. - -## Definition of Done - -Interface behavior matches design intent. States are handled. Patterns are reusable or documented. Tokens in CSS are the system of record until a Google-format `DESIGN.md` exists. - -## Agent Prompt - -Apply Design First to Basilic. - -Read ADR 004, frontend MDX, `packages/ui`, and `packages/ui/src/styles/tokens.css` before building UI. There is no Google-format `DESIGN.md` — do not create a second palette. - -Reuse `@repo/ui`. Do not invent components, hex values, or typefaces without checking the system. Define loading, empty, error, and success states. Validate in the browser across states. Update durable design artifacts when patterns change. Update this instance when paths change. - -## Notes - -**Design vs Journeys:** Journeys define what happens. Design defines how the interface expresses it. - -**Design vs API:** API is the capability boundary. Design is the human-visible expression. `DESIGN.md` is an artifact, not the principle. - -**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/DESIGN.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/DESIGN.md) · [Factory map](../ABOUT.md) · [Frontend](../../apps/docu/content/docs/architecture/frontend.mdx) · [ADR 004](../../apps/docu/content/docs/adrs/004-design-system.mdx) diff --git a/_first/basilic/DOCUMENTATION.md b/_first/basilic/DOCUMENTATION.md index c357fec1..ccde20be 100644 --- a/_first/basilic/DOCUMENTATION.md +++ b/_first/basilic/DOCUMENTATION.md @@ -2,25 +2,18 @@ ## Principle -Keep the context future humans and agents need to decide well in durable project files, not in conversations that disappear. - -## Statement - -I document decisions and context, not obvious code. If someone will need to rediscover it, explain it twice, or guess why we chose this, it belongs in a file. Chat is for coordination. Files are the system of record. Wrong docs are worse than no docs. - -## Outcome - -Consequential decisions, conventions, setup steps, and domain context live in discoverable project files. Documentation matches current behavior or explicitly notes drift. When behavior or assumptions change, the files change in the same work. +See /f-info-architect. ## Artifacts - **Fact:** Product intent: [PRODUCT.md](PRODUCT.md) — brief, feature map, roadmap. Not in `apps/docu` - **Fact:** Adopter technical docs: [`../../apps/docu/content/docs/`](../../apps/docu/content/docs/) — architecture, ADRs, development, testing, deployment +- **Fact:** Generated-project docs snapshot: `docs/basilic/` is assembled from `apps/docu/content/docs` at pack time. Canonical authoring stays in `apps/docu`. The snapshot is not a second source. - **Fact:** How to run: [`../../README.md`](../../README.md) and app/package READMEs — link to docs, no duplication ([docs.mdc](../../.cursor/rules/base/docs.mdc)) - **Fact:** Agents: [`../../AGENTS.md`](../../AGENTS.md); constraints `.cursor/rules/`; skills `.agents/skills/` - **Fact:** Workflow index: [ai-workflow.mdx](../../apps/docu/content/docs/development/ai-workflow.mdx) - **Fact:** Public LLM indexes: `/llms.txt`, `/llms-full.txt` on the docs site -- **Fact:** Portable factory: vendored `../AGENTS.md`, `../ABOUT.md`, `../templates/`. Station specs: `/f-*` from `npx skills add blockmatic/first`. This folder is the Basilic adoption pack, not a second docs site. Essays live in [`blockmatic/first`](https://github.com/blockmatic/first/tree/main/_first/articles). +- **Fact:** Portable factory: vendored `../AGENTS.md`, `../ABOUT.md`, `../README.md`. Station specs: `/f-*` from `npx skills add blockmatic/first`. This folder is the Basilic adoption pack, not a second docs site. Essays live in [`blockmatic/first`](https://github.com/blockmatic/first/tree/main/_first/articles). - **Fact:** Same-change rule: update matching MDX when behavior, commands, or conventions change; update [PRODUCT.md](PRODUCT.md) when goals, feature map, or horizons change - **Fact:** `__dev/` is gitignored scratch. Do not treat it as Fact or as the backlog. Remembered technical decisions go in `apps/docu` or ADRs. Product decisions go in [PRODUCT.md](PRODUCT.md). - **Fact:** Architecture MDX matches this overlay: TypeBox on Fastify generates OpenAPI; shipped path is Vercel + Supabase; PostHog is chosen not installed. Named remaining drift: mobile vs web, PostHog not in the runtime. @@ -33,41 +26,8 @@ Consequential decisions, conventions, setup steps, and domain context live in di - implementation links - trigger for next review: the same PR that changes behavior -## Recipe - -1. Inspect READMEs, `AGENTS.md`, Fumadocs MDX, ADRs, rules, and skills. -2. Compare those files to implementation. Flag contradictions. -3. Identify missing decisions, constraints, setup, domain rules. -4. Propose the smallest useful doc — ADR, MDX section, README pointer. -5. Write product intent in [PRODUCT.md](PRODUCT.md). Write technical docs in `apps/docu` (or the collocated README). One canonical source per fact. -6. Update docs in the same change if behavior or assumptions changed. -7. Remove or archive docs that are wrong. -8. Validate that a new contributor can set up and orient from files without asking in chat. - -## Validation - -- A new contributor can set up and orient from README + docs without asking in chat. -- ADRs exist for consequential architectural decisions, or the decision is deferred. -- Agent instructions reflect how the project works today. -- Chat is not the system of record. -- Generic FIRST files stay free of this repo’s product paths. - -## Definition of Done - -Durable context is written, accurate, and discoverable. Documentation drift is resolved or explicitly tracked. Future work will not need to rediscover what was already decided. - -## Agent Prompt - -Apply Documentation First to Basilic. - -Read root README, `AGENTS.md`, [PRODUCT.md](PRODUCT.md), `apps/docu/content/docs/`, ADRs, and `.cursor/rules` before acting. Compare documentation to implementation. - -Document decisions and conventions — not obvious code. Propose the smallest useful PRODUCT.md, MDX, or README update. When you change behavior, update durable files in the same work. Do not leave load-bearing decisions only in chat. Do not encode this repo’s product facts in factory `principles/` or `../ABOUT.md`. Do not write Basilic product intent into `apps/docu`. - ## Notes -**Documentation vs everything:** Other stations produce decisions. Documentation decides which context must stay durable. - -**Documentation vs Workflow:** Workflow determines when context is created. Documentation preserves it. +Other stations produce decisions. Documentation decides which context must stay durable. Workflow determines when context is created. Do not encode this repo’s product facts in factory `principles/` or `../ABOUT.md`. Do not write Basilic product intent into `apps/docu`. **Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/DOCUMENTATION.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/DOCUMENTATION.md) · [Factory map](../ABOUT.md) · [AI workflow](../../apps/docu/content/docs/development/ai-workflow.mdx) diff --git a/_first/basilic/JOURNEYS.md b/_first/basilic/JOURNEYS.md index 0328d5c8..8ed382d8 100644 --- a/_first/basilic/JOURNEYS.md +++ b/_first/basilic/JOURNEYS.md @@ -2,15 +2,7 @@ ## Principle -Map how someone finishes a job — including errors, permissions, and state — before implementation invents the path from whichever screen shipped first. - -## Statement - -The product is not a collection of screens. It is someone trying to finish a job. I map that job from entry to completion before I trust implementation to fill in the gaps. Happy paths are cheap. The product breaks in the alternates: the error nobody designed, the permission that exists on one route but not another, the state with no exit. - -## Outcome - -Actors, entry points, happy paths, alternates, error paths, permission gates, and completion criteria are documented or explicitly marked unknown. Missing states are visible before code hardens around an incomplete model. +See /f-journeys. ## Artifacts @@ -28,6 +20,17 @@ Actors, entry points, happy paths, alternates, error paths, permission gates, an - **Fact:** Assistant demo jobs (web, in-shell chat): (1) account — `getAccountInfo` + `__render: 'user-info'`; (2) markets — `getMarketSnapshot` + `__render: 'market-card'`. Entry = composer or suggestion. Completion for (1) = `accountRender: true`. Completion for (2) = market card rendered. A text-only reply is **not** either job. No GenUI CTA (`actions: {}`). - **Unresolved:** named journey files beyond auth MDX; mobile completion +Interface: + +- **Fact:** Demo shell brand is **Basilic** (sidebar). Markets home uses `@repo/ui` + `tokens.css` (`text-chart-2` / `text-destructive` for 24h change). No second palette. +- **Fact:** No Google-format `_first/DESIGN.md` yet. Do not invent a second palette. When added, use [DESIGN.md Format](https://raw.githubusercontent.com/google-labs-code/design.md/refs/heads/main/docs/spec.md) at `_first/DESIGN.md` (or one path listed in [../FIRST.md](../FIRST.md)). +- **Fact:** Tokens: [`../../packages/ui/src/styles/tokens.css`](../../packages/ui/src/styles/tokens.css) — semantic colors, sidebar, radius, `@theme inline`, Inter / Poppins / mono +- **Fact:** Components: `@repo/ui` (shadcn/ui, Radix, Tailwind 4). ADR [004](../../apps/docu/content/docs/adrs/004-design-system.mdx). Frontend: [frontend.mdx](../../apps/docu/content/docs/architecture/frontend.mdx) +- **Fact:** Apps consume `@repo/ui`; app-only UI collocated in `apps/web` / `apps/mobile` / `apps/docu` +- **Fact:** Skills: `shadcn-v3`, `tailwind-design-system-v4`, `frontend-design-v1`; playbook `/audit-accessibility`, `/use-shadcn` +- **Fact:** Browser verification across states is the UI bar — not a single default screenshot +- **Unresolved:** Google-format `_first/DESIGN.md` (do not generate from `tokens.css` until written on purpose); motion guidelines; copy patterns beyond component defaults + ```mermaid stateDiagram-v2 unauth[Unauthenticated] @@ -51,6 +54,7 @@ stateDiagram-v2 - errors: invalid/expired codes, `TOKEN_REUSE_DETECTED`, `SESSION_NOT_FOUND` - gates: proxy (web) and Fastify JWT/API key — policy in SECURITY.md - completion: authenticated session; home loads. Wallet link after login is optional, not required. +- reuse: `@repo/ui` + `tokens.css`; browser across states Assistant job (demo, same shape): @@ -63,45 +67,8 @@ Assistant job (demo, same shape): - gates: proxy + Bearer JWT - completion: `user-info` **or** `market-card` rendered. Not “the model replied.” -## Recipe - -1. Inspect authentication and account-linking MDX, `apps/web/proxy.ts`, Fastify auth/account routes, web pages, CLI. -2. Understand the actor and the job — not the screen. -3. Identify missing error exits, permission drift across web/API/CLI, states with no resume. -4. Propose the smallest useful map: one job, entries, gates, completion. -5. Write happy path, then alternates, then errors, then permission gates. -6. Compare the map to implementation. Flag Web3-without-UI and mobile as deferrals. -7. Validate every mapped state traces to code or an explicit deferral. -8. Update auth MDX when flow behavior changes; update this instance. - -## Validation - -- Critical flows have defined error and recovery (refresh fail → login; reuse → revoke). -- Permission checks are consistent across proxy, Fastify, and CLI exclusions. -- Every mapped state traces to implemented behavior or an explicit deferral (mobile, wallet UI). - -## Definition of Done - -Critical flows are mapped with happy, alternate, and error paths documented or explicitly deferred. Implementation matches the map, or the map was updated to reflect a deliberate change. - -## Agent Prompt - -Apply Journeys First to Basilic. - -Read authentication and account-linking MDX, `apps/web/proxy.ts`, Fastify auth/account routes, web auth UI, CLI, and tests. Map actors, entry points, states, errors, and completion. Compare documentation to actual behavior. - -Do not invent UI (including wallet connect). Do not invent a mobile journey. The in-shell assistant account-context job is mapped (completion = `user-info` rendered). Surface missing states before implementing. Policy stays in Security. Propose the smallest useful update to journey artifacts in `apps/docu`. Update this instance when flows change. - -Treat CLI and coding agents as actors. If an actor is unnamed, do not invent a tool for them. - ## Notes -**Journeys vs Product:** Product answers what and why. Journeys answer how someone finishes. - -**Journeys vs Design:** Journeys describe behavior and flow. Design describes how the interface expresses it. - -**Journeys vs Security:** Journeys show where a permission gate occurs. Security owns the permission policy. - -**Journeys vs Data:** Journeys describe actor-visible states. Data owns persisted meaning and lifecycle. +Product answers what and why. Journeys answer how someone finishes and how the interface expresses it. Security owns permission policy. Do not invent wallet-connect or mobile journeys. -**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/JOURNEYS.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/JOURNEYS.md) · [Factory map](../ABOUT.md) · [Authentication](../../apps/docu/content/docs/architecture/authentication.mdx) · [Account linking](../../apps/docu/content/docs/architecture/account-linking.mdx) +**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/JOURNEYS.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/JOURNEYS.md) · [Factory map](../ABOUT.md) · [Authentication](../../apps/docu/content/docs/architecture/authentication.mdx) · [Account linking](../../apps/docu/content/docs/architecture/account-linking.mdx) · [Frontend](../../apps/docu/content/docs/architecture/frontend.mdx) · [ADR 004](../../apps/docu/content/docs/adrs/004-design-system.mdx) diff --git a/_first/basilic/OPERATIONS.md b/_first/basilic/OPERATIONS.md index 1c891f9e..a377f4e5 100644 --- a/_first/basilic/OPERATIONS.md +++ b/_first/basilic/OPERATIONS.md @@ -2,15 +2,7 @@ ## Principle -Decide how you will see, support, and recover the running system before production becomes a black box. - -## Statement - -Shipping is not the end of engineering work. It is when the system meets reality. I want to know what it is doing, what failed, for whom, and how to fix it — without archaeology. A product nobody can operate is a product that will fail quietly. - -## Outcome - -Production behavior is observable at the level the project needs. Logs are structured and useful. Metrics and alerts cover critical failure modes. Runbooks exist for known recovery paths. Product analytics are not the same dashboard as engineering health. A green pipeline after a fix is not verified recovery. +See /f-operations. ## Artifacts @@ -23,7 +15,7 @@ Production behavior is observable at the level the project needs. Logs are struc - **Unresolved:** dashboards and alert rules; runbooks; verify-in-the-running-system after deploy - **Unresolved:** production identity/retries for in-product AI agents -PostHog is Product, not operations. Pino `session_issued` is ops. Product `auth_succeeded` / `auth_failed` are `capture()` calls, not log lines. Do not file a missing collected product event as an ops ticket. +PostHog is Product, not operations. Pino `session_issued` is ops. Do not file a missing collected product event as an ops ticket. ## Minimum Useful Artifact @@ -34,46 +26,8 @@ PostHog is Product, not operations. Pino `session_issued` is ops. Product `auth_ - recovery: **unresolved** as runbooks - verify: **unresolved** (CI green is not this) -## Recipe - -1. Inspect logging MDX, error-handling MDX, `/health`, captureError (log-only), deploy docs. -2. Understand critical failure modes and who notices first. -3. Identify a 500 with no useful log, a deploy that cannot be verified. -4. Propose the smallest useful signal — one log field, one metric, one alert. -5. Write a runbook only for paths that have burned time. -6. Implement. Do not invent product events (Product). -7. Route production bugs through workflow: issue → fix → pipeline → verify in the running system. -8. Update operational MDX when behavior or failure modes change; update this instance. - -## Validation - -- A developer can diagnose a common failure from Pino without guessing. Sentry is not an active sink. -- Alerts fire on real problems, or alerts are marked unresolved. -- Recovery steps are documented and exercised, or marked unresolved. -- Post-fix deploy was verified in production or staging. CI green is not that verification. - -## Definition of Done - -Runtime behavior is observable enough to support and debug. Recovery paths are known. Production feedback can enter the development workflow and close the loop. - -## Agent Prompt - -Apply Operations First to Basilic. - -Read logging and error-handling MDX, `/health`, `@repo/error` / Sentry usage, and deployment docs before changing runtime behavior. Identify observability blind spots. - -Propose the smallest useful logging, metric, or alert improvement. Use structured signals — not `console`. Route fixes through workflow and pipelines. Verify recovery after deploy in the running system. Do not file PostHog gaps as ops. Update this instance when paths change. - ## Notes -**Operations vs Pipelines:** Pipelines move changes into production. Operations understands and runs the system after deployment. - -**Operations vs Security:** Security defines trust and protection. Operations defines visibility and recovery. - -**Operations vs Product:** Product owns whether the bet is working. Operations owns whether the system is healthy. - -**Operations vs Architecture:** Architecture names deployment units. Operations observes and recovers them. - -**Operations vs Data:** Data owns product-state meaning. Operations owns telemetry about runtime health. +Workflow moves changes into production. Operations understands and runs the system after deployment. Security defines trust. Product owns whether the bet is working. Do not invent `console` logging. Do not file PostHog gaps as ops. **Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/OPERATIONS.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/OPERATIONS.md) · [Factory map](../ABOUT.md) · [Logging](../../apps/docu/content/docs/architecture/logging.mdx) · [Error handling](../../apps/docu/content/docs/architecture/error-handling.mdx) diff --git a/_first/basilic/PIPELINES.md b/_first/basilic/PIPELINES.md index fa4b5b8a..938d14ee 100644 --- a/_first/basilic/PIPELINES.md +++ b/_first/basilic/PIPELINES.md @@ -2,75 +2,27 @@ ## Principle -Treat automated validation and delivery as part of the development feedback loop — including the agent feedback loop — not as a ritual after the real work. - -## Statement - -A change is not done when it compiles on my machine. It is done when it passes the same checks everyone else relies on, and when it can reach the environment it needs to reach. For agents, CI is ground truth: implement, run checks, read failures, fix or escalate, run again. I keep the path from source to deployable explicit at the level the project actually needs. - -## Outcome - -Changes flow through an automated path: format → lint → typecheck → test → build → preview (if applicable) → deploy → verify. The commit stage builds the artifact once. Later stages promote that artifact; they do not rebuild a different one per environment. Agents can interpret CI failure output and act. +See /f-pipelines. ## Artifacts -- **Fact:** [github-actions.mdx](../../apps/docu/content/docs/deployment/github-actions.mdx) -- **Fact:** [`.github/workflows/lint.yml`](../../.github/workflows/lint.yml) — Biome/ESLint/types. FIRST factory validation lives in [`blockmatic/first`](https://github.com/blockmatic/first) -- **Fact:** [`.github/workflows/security.yml`](../../.github/workflows/security.yml), [`.github/workflows/deepsec.yml`](../../.github/workflows/deepsec.yml) -- **Fact:** Path-filtered: `api-e2e.yml` (OpenAPI drift + cov), `web-e2e.yml`, `packages-test.yml` -- **Fact:** Mobile: `mobile-build.yml`, `mobile-preview.yml`, `mobile-pr-preview.yml` ([mobile-cicd.mdx](../../apps/docu/content/docs/deployment/mobile-cicd.mdx)) -- **Fact:** Local: `pnpm qa` via [`../../scripts/run-qa.mjs`](../../scripts/run-qa.mjs) — checktypes → lint → generate + drift → build → unit → e2e (`SKIP_BUILD=1`) -- **Fact:** Vercel git deploys web/api/docu ([vercel.mdx](../../apps/docu/content/docs/deployment/vercel.mdx)). CI does **not** deploy. Preview migrate gated unless `RUN_PG_MIGRATE=true`. -- **Fact:** `web-e2e` chat project runs only when a non-placeholder Anthropic key is present (`hasRealAnthropicKey()`: empty, `sk-ant-xxx`, and `sk-ant-dummy*` omit it); auth/dashboard E2E still run on forks -- **Fact:** R0 is documentation alignment. It does not require a GitHub Release or a version bump. Preview deploys still run from git as usual. -- **Unresolved:** commit-stage artifact identity and promote-without-rebuild (hosts rebuild from git) +- **Fact:** Vercel git deploys `apps/web`, `apps/api`, and `apps/docu`. EAS workflows deploy mobile. Those are app delivery, not Basilic npm distribution. +- **Fact:** Basilic distribution is **Release Please** + `create-basilic` npm tarball. Version lives on the repo root (`package.json` `version`) and is synced into `tools/create-basilic/package.json`. Tag format `vX.Y.Z`. +- **Fact:** A maintainer merge of the release PR is the publish decision. After the tag, `publish-create-basilic.yml` assembles and packs **once**, tests that tarball, publishes it with npm trusted publishing (`id-token` only on that job), and attaches the same file to the GitHub Release. +- **Fact:** Artifact identity: SHA-256 of the packed tarball. Do not rebuild at publish. Retry GitHub asset upload from the retained artifact; never overwrite an npm version. +- **Fact:** Preview: manual `0.1.0-next.1` with npm dist-tag `next`, not `latest`. Stable `1.0.0` only after Product Ready from `npx create-basilic@`. +- **Fact:** Generated repos do not receive Release Please, the publish workflow, or npm credentials. +- **Fact:** R0 Quality (Product Ready) does not require a GitHub Release. Distribution releases are a separate pipeline. ## Minimum Useful Artifact -- triggers: every PR for lint/security; path filters for e2e/packages; EAS on mobile paths -- local mirror: `pnpm qa`, `pnpm lint`, `pnpm checktypes` -- artifact: **unresolved** (git SHA on Vercel/EAS) -- promotion: preview on PR, prod on main — rebuild, not promote -- failures: `gh pr checks` / `gh run view` (`/fix-github-actions`); never GitHub MCP for Actions logs - -## Recipe - -1. Inspect `.github/workflows/`, `turbo.json`, deployment MDX, `pnpm qa`. -2. Understand path from change to Vercel/EAS/API host. -3. Identify missing checks, flaky jobs, unreadable logs. -4. Propose the smallest pipeline improvement. -5. Implement. Prefer existing `pnpm` scripts. -6. Verify locally with the same commands CI uses when possible. -7. Read CI output with `gh`. Fix or escalate. -8. Document deploy steps in deployment MDX if they changed. - -## Validation - -- CI passes on the change branch. -- Local `pnpm qa` / targeted scripts catch the same class of failure. -- A green agent sandbox is not GitHub Actions. -- Deploy path is documented. Promote-without-rebuild is unresolved, not pretended. - -## Definition of Done - -The change is validated by automated pipelines and is deployable through the project's defined path. Pipeline config is updated if new checks were added. - -## Agent Prompt - -Apply Pipelines First to Basilic. - -Read `.github/workflows/`, `turbo.json`, deployment MDX, and root README scripts before changing delivery. Use existing commands — `pnpm qa` when the change warrants it. - -When implementation completes, ensure CI would pass. Read failures with `gh` (not GitHub MCP). Do not treat a green local sandbox as the pipeline. Distinguish pipeline failures from quality criteria. Update deployment docs when delivery changes. Update this instance when workflows or commands change. +- commit that produced the tarball: the release tag SHA +- packed file + checksum: GitHub Release assets and the npm tarball +- promotion: maintainer merge → pack once → npm + GitHub asset +- rollback: patch + deprecate; never rewrite tags ## Notes -**Pipelines vs Workflow:** Pipelines automate validation and delivery. Workflow defines how humans and agents respond. - -**Pipelines vs Architecture:** Architecture defines deployment units. Pipelines build and deliver them. - -**Pipelines vs Quality:** Quality names the bar. Pipelines run it. - -**Pipelines vs Operations:** Pipelines get changes into production. Operations runs what arrived. +Workflow runs CI on feature PRs. Pipelines own versioned artifacts. Operations runs what was promoted. -**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/PIPELINES.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/PIPELINES.md) · [Factory map](../ABOUT.md) · [GitHub Actions](../../apps/docu/content/docs/deployment/github-actions.mdx) +**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/PIPELINES.md) · [Publishing](../../apps/docu/content/docs/deployment/publishing.mdx) · [GitHub Actions](../../apps/docu/content/docs/deployment/github-actions.mdx) · [ADR 012](../../apps/docu/content/docs/adrs/012-scaffolding-and-releases.mdx) diff --git a/_first/basilic/PRODUCT.md b/_first/basilic/PRODUCT.md index 92ba6d48..a4dd3870 100644 --- a/_first/basilic/PRODUCT.md +++ b/_first/basilic/PRODUCT.md @@ -2,15 +2,7 @@ ## Principle -Define what you are building, for whom, why it is worth building, and how you will know — before implementation becomes the specification. - -## Statement - -I do not let the codebase become the product brief. Before I change meaningful behavior, I want a file that names the problem, who has it, why it is worth building, what we are not building, how it reaches people, and how we will know. Implementation can reveal a better option. It should not invent the goal. - -## Outcome - -The project has an inspectable answer to what, why, and how we will know. Non-goals, GTM, success metrics, and the tracking plan are written or explicitly unresolved. Named metrics have events, or are marked unmeasured. When the product is a business, market size and unit economics are stated as measured or as hypotheses. +See /f-product. ## Artifacts @@ -18,7 +10,7 @@ The project has an inspectable answer to what, why, and how we will know. Non-go - **Fact:** [`../../README.md`](../../README.md) — MIT fork-and-run TypeScript fullstack starter (Fastify, OpenAPI, Next, Expo scaffold). No Wagmi, no first-class OpenAI SDK, no web wallet UI - **Fact:** [`../../apps/docu/content/docs/`](../../apps/docu/content/docs/) — adopter technical docs (architecture, ADRs, development, testing, deployment). Not a product site - **Fact:** Two audiences: **adopters** (developers using the starter) and **demo users** (web markets `/`, headlines strip, settings, in-shell assistant; auth is the shipped job) -- **Fact:** GTM: clone or GitHub **Use this template** + [Getting Started](../../apps/docu/content/docs/development/index.mdx) (`db:start`, `pnpm reset`) + first local login (`ALLOW_TEST` + `test@test.ai`). Bar: [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). After you own the copy: [After fork](../../apps/docu/content/docs/development/after-fork.mdx). Finance **N/A** (toolkit) +- **Fact:** GTM: `npx create-basilic@latest my-app` for a new product; clone or GitHub **Use this template** still works; **fork** to contribute upstream. Then [Getting Started](../../apps/docu/content/docs/development/index.mdx) (`db:start`, `pnpm reset`) + first local login (`ALLOW_TEST` + `test@test.ai`). Bar: [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). After you own the copy: [After fork](../../apps/docu/content/docs/development/after-fork.mdx). Finance **N/A** (toolkit) - **Fact:** Owner: Gabo Esquivel - **Fact:** New-device sign-in alerts are transactional email via Fastify `emailProvider` + `@repo/email`, not a notification product - **Fact:** Not a billed SaaS in files. Do not invent TAM/LTV @@ -28,7 +20,7 @@ The project has an inspectable answer to what, why, and how we will know. Non-go - **Fact:** PD (Markets + GenAI artifacts) is shipped on the feature map: CoinGecko or mock, `getMarketSnapshot` / `market-card` - **Unresolved:** PostHog install / consent / retention; keep / iterate / kill board; whether adopters copy `lib/analytics` -`pnpm qa` going green is Pipelines, not product success. Quality for R0 is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). +`pnpm qa` going green is Workflow, not product success. Quality for R0 is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). ## Minimum Useful Artifact @@ -36,52 +28,14 @@ The project has an inspectable answer to what, why, and how we will know. Non-go - users: adopting developers; demo end users on web auth and dashboard - goal: portable starter with self-hosted Web2 auth and Cursor-first workflow (Web3 on API only) - non-goals: Brief → Non-goals (R0) in this file -- audience/channel/first use: clone or Use this template + Getting Started (`db:start`, `pnpm reset`) + first local login (`ALLOW_TEST` + `test@test.ai`) ([Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx)) +- audience/channel/first use: `npx create-basilic@latest` (new product) or clone/template + Getting Started (`db:start`, `pnpm reset`) + first local login (`ALLOW_TEST` + `test@test.ai`) ([Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx)) - metrics: auth and assistant jobs **instrumented, not collected** - events: `auth_succeeded`, `auth_failed`, `assistant_turn` — specified + instrumented, no sink - owners: Gabo Esquivel -## Recipe - -1. Inspect README, this file, analytics ADR, issues, running web/API, and claimed metrics. -2. Understand shipped vs claimed (starter vs product, GTM, measurement). -3. Identify missing users, missing goal, success that cannot fail, named metrics with no events. -4. Propose the smallest useful update to this file or README — not a parallel fake PRD, not `apps/docu` Product pages. -5. Make decisions explicit, or name them unresolved. Do not hide them in code. -6. When a change can move a metric, ship the event in the same work — or mark unmeasured. -7. After use, compare metric to target and record keep / iterate / kill — when metrics exist. -8. Update this file if the bet, GTM, feature map, or analytics changed. - -## Validation - -- A new maintainer can answer what we are building from this file + README without `__dev/` or Fumadocs Product pages -- Success metrics can fail. They are not CI green. Auth/assistant remain unmeasured (no sink) -- GTM is clone or Use this template + Getting Started. Product Ready is that path, not CI green. Finance N/A -- No silent product decisions in code without a note or open question - -## Definition of Done - -Product intent is documented or explicitly deferred with named owners. Implementation aligns with stated goals and non-goals, or this file was updated. Success that was claimed is either instrumented or marked unmeasured. - -## Agent Prompt - -Apply Product First to Basilic. - -Read root README, this file, analytics MDX and ADR 011, and what the web, mobile, and API actually do. PostHog is not installed. `capture()` is a no-op — do not claim events are collected or measured. PD is shipped on the feature map. - -Preserve intentional existing product choices. Do not silently decide scope, priorities, pricing, TAM, LTV, or event names. This is a toolkit — say so rather than filling finance blanks. - -Separate `pnpm qa` from post-launch success. Propose the smallest useful update to this file or README. Do not write Basilic product intent into `apps/docu`. - ## Notes -**Product vs Journeys:** Product names what, why, and how we will know. Journeys name how someone finishes. - -**Product vs Quality:** Product names the outcome after use. Quality names the bar that gates a release. - -**Product vs Operations:** Product owns events, funnels, activation. Operations owns logs, traces, error rates, alerts, recovery. - -**Product vs Data:** Product names events and outcomes to measure. Data owns canonical domain meaning, authority, lifecycle, and evolution. +Product names what, why, and how we will know. Journeys name how someone finishes. Quality names the bar that gates a release. Workflow runs `pnpm qa`. Do not write Basilic product intent into `apps/docu`. This is a toolkit — do not invent TAM or LTV. **Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/PRODUCT.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/PRODUCT.md) · [Factory map](../ABOUT.md) · [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx) · [Analytics](../../apps/docu/content/docs/architecture/analytics.mdx) · [ADR 011](../../apps/docu/content/docs/adrs/011-product-analytics.mdx) @@ -93,7 +47,7 @@ Owner until this file says otherwise: **Gabo Esquivel**. ### Two audiences -**Adopters** clone the repo or use GitHub **Use this template**, run the stack locally, and copy patterns. First successful use is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx): clone → [Getting Started](../../apps/docu/content/docs/development/index.mdx) (`db:start`, `pnpm reset`, `pnpm dev`) → `ALLOW_TEST` + `test@test.ai` to `/`. After they own the copy: [After fork](../../apps/docu/content/docs/development/after-fork.mdx). +**Adopters** run `npx create-basilic@latest my-app` (or clone / GitHub **Use this template**), run the stack locally, and copy patterns. First successful use is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx): generate or clone → [Getting Started](../../apps/docu/content/docs/development/index.mdx) (`db:start`, `pnpm reset`, `pnpm dev`) → `ALLOW_TEST` + `test@test.ai` to `/`. After they own the copy: [After fork](../../apps/docu/content/docs/development/after-fork.mdx). Forks remain the path to contribute to Basilic. **Demo users** sign in to the web app. The shipped job is auth (sessions, API keys, settings). Markets, a headlines strip, and the in-shell assistant are demo chrome. See Feature map below. @@ -109,12 +63,12 @@ A portable typed API plus web, mobile scaffold, and docs so an adopting develope - Next.js Cache Components on - First-class OpenAI SDK (chat uses Anthropic → OpenRouter → Ollama) - GCP/AWS as the shipped deploy path (Vercel + Supabase is documented) -- FIRST CLI, `first.json`, or a 13th FIRST station. `npx skills add blockmatic/first` (`/f-*`) is in; do not ship a skill that mixes spec and instance +- FIRST CLI, `first.json`, or an 11th FIRST station. `npx skills add blockmatic/first` (`/f-*`) is in; do not ship a skill that mixes spec and instance - Billed SaaS metrics ### How we will know -`pnpm qa` going green is **Pipelines**, not product success. The R0 Quality bar is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). Horizons: Roadmap below. +`pnpm qa` going green is **Workflow**, not product success. The R0 Quality bar is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). Horizons: Roadmap below. Auth (`auth_succeeded` / `auth_failed`) and assistant (`assistant_turn` with `accountRender`) are **instrumented** via `capture()` and **not collected** — PostHog is chosen, not installed. Those jobs are therefore **unmeasured**. Factory GTM, demo surface quality, and cost-per-job are unmeasured. @@ -126,9 +80,10 @@ Status is what the tree does today, not a wish list. Horizons: Roadmap below. ### Spine (fork-and-run) -Must work after clone → `pnpm setup` → `db:start` → `pnpm reset` → `pnpm dev`. +Must work after `npx create-basilic@latest` (or clone) → `pnpm setup` → `db:start` → `pnpm reset` → `pnpm dev`. - Fastify TypeBox API → generated OpenAPI → `@repo/core` / handwritten `@repo/react` +- **create-basilic** generator (`tools/create-basilic`) — new products; forks stay for upstream contributions - Auth: magic link (Resend **or** copied local `ALLOW_TEST=true` + `test@test.ai`) → cookies → `/` - Optional OAuth (unconfigured = disabled / 503) - Passkeys, sessions, API keys `bask_`, Settings profile and security @@ -165,7 +120,7 @@ Signed-in demo is **Markets + GenAI artifacts**: CoinGecko or mock, `getMarketSn Horizons, not a sprint board. Work items live in [GitHub Issues](https://github.com/blockmatic/basilic/issues). `__dev/` is gitignored scratch — not Fact, not the backlog. -R0 is **documentation alignment**. It does not need a semver bump or a GitHub Release. Pipelines still run on PRs; Quality is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). +R0 is **documentation alignment**. It does not need a semver bump or a GitHub Release. Workflow still runs CI on PRs; Quality is [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx). ### R0 — docs, honesty, onboarding @@ -174,6 +129,13 @@ R0 is **documentation alignment**. It does not need a semver bump or a GitHub Re - MIT `LICENSE`; GitHub Template; [After fork](../../apps/docu/content/docs/development/after-fork.mdx) - [Product Ready](../../apps/docu/content/docs/testing/product-ready.mdx): `db:start` + `pnpm reset` before `pnpm dev`; copied env `ALLOW_TEST=true` +### R-launch — generator and distribution + +- `npx create-basilic@latest` ships an independent API/web/mobile repo (no docu app, no generator) +- Release Please versions Basilic; maintainer merge publishes the npm tarball +- Stable **1.0.0** only after Product Ready from the published package +- Human gates: npm `create-basilic` trusted publisher, GitHub App, squash `PR_TITLE`+`PR_BODY`, who may merge release PRs + ### R-demo — Markets + GenAI artifacts Named even if the implementation PR is still in flight: CoinGecko or checked-in mock, `getMarketSnapshot` + market-card catalog, Markets as signed-in home. No wallet UI. No Fastify markets CRUD. No extra CI workflow. See Feature map above. @@ -191,4 +153,4 @@ R1 is still a choice among wallet UI, mobile client, and observability — not a ### Not now -FIRST CLI, `first.json`, billed SaaS / TAM-LTV, first-class OpenAI SDK, GCP/AWS as the shipped deploy path, Cache Components on, 13th FIRST station, root `PRODUCT.md` / `ROADMAP.md`. `/f` via `npx skills add blockmatic/first` is in. +FIRST CLI, `first.json`, billed SaaS / TAM-LTV, first-class OpenAI SDK, GCP/AWS as the shipped deploy path, Cache Components on, 11th FIRST station, root `PRODUCT.md` / `ROADMAP.md`. `/f` via `npx skills add blockmatic/first` is in. diff --git a/_first/basilic/QUALITY.md b/_first/basilic/QUALITY.md index ace9d40e..9e83f127 100644 --- a/_first/basilic/QUALITY.md +++ b/_first/basilic/QUALITY.md @@ -2,15 +2,7 @@ ## Principle -Name what good means — acceptance, tests, evals, performance — before anyone optimizes toward an undefined target. - -## Statement - -I do not ask anyone to "make it better" without saying what better means. Tests prove deterministic behavior. Evals cover probabilistic output. Performance budgets beat vibes. Name the bar before you optimize, or you ship something that looks done and still fails users. - -## Outcome - -Acceptance criteria exist for features that matter. Tests protect critical paths. AI features with probabilistic output have evals where appropriate. Performance-sensitive areas have budgets. Humans and agents know what done means beyond "it compiles." +See /f-quality. ## Artifacts @@ -22,7 +14,7 @@ Acceptance criteria exist for features that matter. Tests protect critical paths - **Fact:** AI: contract tests hard; remote may `ctx.skip()` when key missing or 402 — never return early without skip (soft-pass forbidden). With real key, 502/503/504 fail. - **Fact:** Coverage: `pnpm --filter @repo/api test:cov` uploaded; **no floors** in CI - **Fact:** Playbooks: `write-api-test`, `write-unit-tests`, `use-tdd`, `run-all-tests-and-fix` -- **Fact:** Product Ready (R0 bar) is the fork-and-run checklist on [product-ready.mdx](../../apps/docu/content/docs/testing/product-ready.mdx), not CI green. Pipelines run CI. +- **Fact:** Product Ready (R0 bar) is the fork-and-run checklist on [product-ready.mdx](../../apps/docu/content/docs/testing/product-ready.mdx), not CI green. Workflow runs CI. - **Unresolved:** eval datasets for `/ai/chat` and `/ai/generate`; performance budgets; visual regression ## Minimum Useful Artifact @@ -33,43 +25,8 @@ Acceptance criteria exist for features that matter. Tests protect critical paths - command: `pnpm --filter @repo/api test:unit`, `pnpm test:e2e` - on fail: fix or escalate; do not skip remote AI without `ctx.skip()` -## Recipe - -1. Inspect testing MDX, existing Vitest/Playwright, and CI quality jobs. -2. Understand what “good” means for this change. -3. Identify unprotected critical paths or AI without evals. -4. Propose the smallest useful quality artifact before or alongside implementation. -5. Write criteria that assert behavior. API: one status per `inject()`. -6. Run existing validation — do not invent a parallel suite. -7. Fix failures or escalate when the bar needs a product decision. -8. Update testing MDX when criteria change; update this instance. - -## Validation - -- Acceptance criteria are testable or demonstrable. -- Critical paths have automated protection where the project supports it. -- Failures produce actionable signal. -- AI evals cover user-dependent behaviors, or are marked unresolved (skip is not a pass). -- CI going green is Pipelines. Meeting the bar is Quality. - -## Definition of Done - -Stated quality criteria are met and verified by the project's existing validation. Regressions are caught or explicitly accepted with documented rationale. - -## Agent Prompt - -Apply Quality First to Basilic. - -Read `apps/docu/content/docs/testing/`, existing `*.spec.ts` / Playwright specs, and CI gates before implementing. Define what “good” means for this change. - -Write or update tests alongside implementation. Use existing commands. For `/ai/*`, do not treat a wrapper unit test as an eval. Never soft-pass remote AI tests. Preserve group-entry and catalog-error patterns. Fix failures or escalate. Update testing docs when criteria change. Update this instance when commands change. - ## Notes -**Quality vs Pipelines:** Quality defines what should be validated. Pipelines run the validation. - -**Quality vs Product:** Product defines success after use. Quality defines the bar that gates a release. - -**Quality vs Data:** Data owns domain invariants. Quality owns release gates and eval datasets. +Quality defines what should be validated. Workflow runs the validation. Product defines success after use. For `/ai/*`, do not treat a wrapper unit test as an eval. Never soft-pass remote AI tests. **Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/QUALITY.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/QUALITY.md) · [Factory map](../ABOUT.md) · [Testing](../../apps/docu/content/docs/testing/index.mdx) diff --git a/_first/basilic/README.md b/_first/basilic/README.md index 8ba6d119..889a3d52 100644 --- a/_first/basilic/README.md +++ b/_first/basilic/README.md @@ -1,8 +1,8 @@ # Basilic instances -Operational specs for applying FIRST in this monorepo. Not a second factory. Product intent lives in [PRODUCT.md](PRODUCT.md). Technical facts live in `apps/docu/content/docs/` (architecture, ADRs, how-to), TypeBox routes, OpenAPI (generated), and package READMEs. `apps/docu` is not a product site. +Operational overlays for applying FIRST in this monorepo. Not a second factory. Product intent lives in [PRODUCT.md](PRODUCT.md). Technical facts live in `apps/docu/content/docs/` (architecture, ADRs, how-to), TypeBox routes, OpenAPI (generated), and package READMEs. `apps/docu` is not a product site. -This folder is this repository’s adoption pack. Factory source: [`blockmatic/first`](https://github.com/blockmatic/first). Skip this directory when copying FIRST elsewhere. Copy `README.md`, `ABOUT.md`, `AGENTS.md`, and `templates/` into `_first/`. Add `FIRST.md`. Install `npx skills add blockmatic/first`. Do not copy this folder. +This folder is this repository’s adoption pack. Factory source: [`blockmatic/first`](https://github.com/blockmatic/first). Skip this directory when copying FIRST elsewhere. Copy `README.md`, `ABOUT.md`, and `AGENTS.md` into `_first/`. Add `FIRST.md`. Write overlays only for stations listed as In. Install `npx skills add blockmatic/first`. Do not copy this folder. ## Load order @@ -26,15 +26,13 @@ Use `/f-*` for the operational spec. Essays live on the FIRST site. ## Format sync -When factory template headings change, update every station file here in the same session. Do not teach this folder to the factory validator in `blockmatic/first`. - -Required overlay `##` headings: +Required overlay `##` headings (see [`../ABOUT.md`](../ABOUT.md)): `Principle` · `Artifacts` · `Minimum Useful Artifact` · `Notes` -Existing files in this folder may still carry the full factory spec headings until they are thinned. Product adds Brief, Feature map, and Roadmap after Notes. +Product adds Brief, Feature map, and Roadmap after Notes. Do **not** paste Recipe, Statement, Outcome, Validation, Definition of Done, or Agent Prompt from the `/f-*` spec. -Filenames match the twelve stations in order: PRODUCT, JOURNEYS, DESIGN, ARCHITECTURE, DATA, API, DOCUMENTATION, WORKFLOW, PIPELINES, QUALITY, SECURITY, OPERATIONS. +Filenames match the ten stations in order: PRODUCT, JOURNEYS, ARCHITECTURE, DATA, API, DOCUMENTATION, WORKFLOW, QUALITY, SECURITY, OPERATIONS. ## Link convention @@ -53,19 +51,17 @@ From a station file in this folder: |---:|---|---| | 1 | Product | [PRODUCT.md](PRODUCT.md) | | 2 | Journeys | [JOURNEYS.md](JOURNEYS.md) | -| 3 | Design | [DESIGN.md](DESIGN.md) | -| 4 | Architecture | [ARCHITECTURE.md](ARCHITECTURE.md) | -| 5 | Data | [DATA.md](DATA.md) | -| 6 | API | [API.md](API.md) | -| 7 | Documentation | [DOCUMENTATION.md](DOCUMENTATION.md) | -| 8 | Workflow | [WORKFLOW.md](WORKFLOW.md) | -| 9 | Pipelines | [PIPELINES.md](PIPELINES.md) | -| 10 | Quality | [QUALITY.md](QUALITY.md) | -| 11 | Security | [SECURITY.md](SECURITY.md) | -| 12 | Operations | [OPERATIONS.md](OPERATIONS.md) | - -Do not invent TAM, event taxonomies, or SLOs to complete a template. Label **Fact**, **Drift**, and **Unresolved** under Artifacts. +| 3 | Architecture | [ARCHITECTURE.md](ARCHITECTURE.md) | +| 4 | Data | [DATA.md](DATA.md) | +| 5 | API | [API.md](API.md) | +| 6 | Documentation | [DOCUMENTATION.md](DOCUMENTATION.md) | +| 7 | Workflow | [WORKFLOW.md](WORKFLOW.md) | +| 8 | Quality | [QUALITY.md](QUALITY.md) | +| 9 | Security | [SECURITY.md](SECURITY.md) | +| 10 | Operations | [OPERATIONS.md](OPERATIONS.md) | + +Do not invent TAM, event taxonomies, or SLOs to complete an overlay. Label **Fact**, **Drift**, and **Unresolved** under Artifacts. ## Overlay as delta -Keep the required `##` headings. Fill Artifacts with pointers and facts. Do **not** paste the generic Recipe, Agent Prompt, or Statement from the `/f-*` spec. Product is the exception: [PRODUCT.md](PRODUCT.md) is overlay and canonical brief (Brief, Feature map, Roadmap after Notes). Other stations point at `apps/docu` for technical facts. `__dev/` is scratch until it graduates into docs or this overlay. +Keep the required `##` headings. Fill Artifacts with pointers and facts. Product is the exception: [PRODUCT.md](PRODUCT.md) is overlay and canonical brief. Other stations point at `apps/docu` for technical facts. `__dev/` is scratch until it graduates into docs or this overlay. diff --git a/_first/basilic/SECURITY.md b/_first/basilic/SECURITY.md index 5d16c50c..c6e02b5f 100644 --- a/_first/basilic/SECURITY.md +++ b/_first/basilic/SECURITY.md @@ -2,15 +2,7 @@ ## Principle -Identify what you are trusting, protecting, exposing, and allowing — proportionally to actual risk — before architecture makes security assumptions expensive to change. - -## Statement - -Security is not a phase at the end. It is a set of decisions about boundaries: who can access what, what data is sensitive, what inputs are untrusted, what an agent is allowed to touch. I scale rigor to risk — an internal tool and a regulated financial product do not get the same bar. I also do not pretend risk is zero. - -## Outcome - -Trust boundaries are documented. Auth rules are consistent and enforced at boundaries. Secrets are not committed and not logged. External inputs are validated. Agent permissions are scoped: read-only where possible, destructive actions gated, secrets minimized, human approval for high-risk operations. +See /f-security. ## Artifacts @@ -27,6 +19,7 @@ Trust boundaries are documented. Auth rules are consistent and enforced at bound - **Fact:** Secrets: `ENCRYPTION_KEY`, `JWT_SECRET`, OAuth client secrets, `RESEND_API_KEY`, AI keys, `SENTRY_DSN` (unused until Sentry is re-enabled), `DATABASE_URL`, `AI_GATEWAY_API_KEY`, `EXPO_TOKEN` - **Unresolved:** named threat model; data classification list; in-product AI tool permission matrix - **Unresolved:** accepted-risk register with owner and next review +- **Unresolved:** refresh ownership / concurrency protocol (Architecture names the dual-path drift) Treat coding agents as a service account: least privilege, no secrets, human approval for destructive or trust-boundary edits. @@ -38,46 +31,8 @@ Treat coding agents as a service account: least privilege, no secrets, human app - agent: read-only inspection default; stop for secrets, destructive ops, auth policy changes - accepted risks: `httpOnly: false` cookie (documented); in-memory rate limit; **unresolved** as a dated register -## Recipe - -1. Inspect security MDX, auth MDX, `apps/web/proxy.ts`, Fastify auth/CORS/security plugins, scanners. -2. Understand what is stored, transmitted, logged, and exposed to agents (coding and `/ai/*`). -3. Identify auth drift across web/API/CLI, secrets in logs, untrusted input without TypeBox. -4. Propose the smallest security fix or documentation update. -5. Scope agent permissions like a role. Gate destructive work. -6. Implement with existing scanners and auth patterns. Do not invent a parallel auth system. -7. Validate with `pnpm security:check` and existing auth tests. Failures fixed or explicitly accepted. -8. Update security MDX when the trust model changes; update this instance. - -## Validation - -- Auth enforced at proxy and Fastify, not ad hoc per handler. -- No secrets in code, logs, or agent-accessible files without justification. -- Destructive and high-risk operations require human approval. -- Security scans pass, or failures are accepted with rationale. - -## Definition of Done - -Trust boundaries and permissions are documented and implemented consistently. Agent access is scoped. Identified risks are addressed or explicitly accepted at the appropriate level. - -## Agent Prompt - -Apply Security First to Basilic. - -Read security and authentication MDX, `apps/web/proxy.ts`, Fastify auth, CORS, and scanners before changing trust boundaries. Inspect implementation; do not assume docs match. - -Prefer read-only inspection. Avoid secrets. Require a human for destructive or security-consequential changes. Treat yourself as a service account. Do not define TypeBox shapes here (API) or journey maps (Journeys). - -Propose the smallest useful security fix or documentation update. Use existing scanners. Update durable security artifacts when the trust model changes. Update this instance when paths change. - ## Notes -**Security vs Operations:** Security defines trust, protection, and permissions. Operations defines runtime visibility and recovery. - -**Security vs API:** API defines contracts at boundaries. Security defines who may invoke them and what they may access. - -**Security vs Architecture:** Architecture maps trust boundaries. Security defines protection and authorization policy across them. - -**Security vs Data:** Data maps classification, copies, retention, and deletion. Security owns access and protection policy. +Security defines trust, protection, and permissions. API defines contracts. Architecture maps trust boundaries. Operations defines runtime visibility. Do not define TypeBox shapes here or journey maps here. **Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/SECURITY.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/SECURITY.md) · [Factory map](../ABOUT.md) · [Security](../../apps/docu/content/docs/architecture/security.mdx) · [Authentication](../../apps/docu/content/docs/architecture/authentication.mdx) diff --git a/_first/basilic/WORKFLOW.md b/_first/basilic/WORKFLOW.md index b2024870..70f35cc4 100644 --- a/_first/basilic/WORKFLOW.md +++ b/_first/basilic/WORKFLOW.md @@ -2,71 +2,46 @@ ## Principle -Make the path from intent to validated change explicit enough that humans, agents, and automation can cooperate without reconstructing the process every time. - -## Statement - -I care less about which methodology name is on the wall and more about whether work can move from idea to shipped, validated change. Who decides what? Where does state live? When does a human approve? If the path is only in people's heads, agents cannot help and humans cannot scale. - -## Outcome - -Work flows through a recognizable path: idea → plan → implement → review → pipeline signals → approval → release → learning. Handoffs have inputs and outputs. Work state lives in issues, tasks, or PRs — not only in chat. Human approval is explicit for destructive, security-sensitive, or product-consequential changes. +See /f-workflow. ## Artifacts - **Fact:** Work state: GitHub Issues and pull requests. There is no `BACKLOG.md`. `__dev/` is gitignored scratch, not the backlog. -- **Fact:** Path: plan (`/plan-feature`) → review → implement → `/git-commit` → `/git-create-pr` → CI + CodeRabbit → `/retro`. Prefer `/git-create-pr` (description + labels) over `/exec-push`. +- **Fact:** Path: plan (`/b-plan-feature`) → review → `/b-build` → `/b-git-commit` → `/b-git-create-pr` → CI + CodeRabbit → `/b-retro`. Use `/b-exec-push` only when the full implementation-to-PR path is requested. - **Fact:** Index: [ai-workflow.mdx](../../apps/docu/content/docs/development/ai-workflow.mdx) -- **Fact:** Playbooks: `.agents/skills/workflow/` — `plan-feature`, `exec-push`, `git-commit`, `code-review`, `deslop`, `retro`, `git-create-pr` +- **Fact:** Playbooks: `.agents/skills/b/` — `/b` dispatcher and `/b-*` children; shared authoring and completion references are packaged inside that tree - **Fact:** Consequential decisions: product intent in [PRODUCT.md](PRODUCT.md); technical in ADRs and `apps/docu` -- **Fact:** Git: default global user; Conventional Commits; never `--no-verify`; never Co-authored-by trailers ([git.mdc](../../.cursor/rules/base/git.mdc)) +- **Fact:** Git: default global user; Conventional Commits; never `--no-verify`; never Co-authored-by trailers ([git.mdc](../../.cursor/rules/base/git.mdc)). Squash-merge uses `PR_TITLE` + `PR_BODY` so `BREAKING CHANGE:` footers survive. Conventional PR titles are gated. +- **Fact:** Advisory `/b-release-review` playbook. AI does not bump versions, merge release PRs, or hold npm credentials. - **Fact:** Human gates: product scope, secrets/trust boundaries, destructive ops ([`../AGENTS.md`](../AGENTS.md)) - **Fact:** Models (docs): Grok 4.6 plan/implement; Sol long-horizon; Composer 2.5 mechanical. In-app chat is a product model, not this workflow. +Automated path: + +- **Fact:** [github-actions.mdx](../../apps/docu/content/docs/deployment/github-actions.mdx) +- **Fact:** [`.github/workflows/lint.yml`](../../.github/workflows/lint.yml) — Biome/ESLint/types. FIRST factory validation lives in [`blockmatic/first`](https://github.com/blockmatic/first) +- **Fact:** [`.github/workflows/security.yml`](../../.github/workflows/security.yml), [`.github/workflows/deepsec.yml`](../../.github/workflows/deepsec.yml) +- **Fact:** Path-filtered: `api-e2e.yml` (OpenAPI drift + cov), `web-e2e.yml`, `packages-test.yml` +- **Fact:** Always-reportable: `pr-title.yml`, `scaffold.yml` (classification + generator tests), `release-impact.yml`. Full generated `pnpm qa` on release-please PRs (`scaffold-acceptance.yml`). +- **Fact:** Mobile: `mobile-build.yml`, `mobile-preview.yml`, `mobile-pr-preview.yml` ([mobile-cicd.mdx](../../apps/docu/content/docs/deployment/mobile-cicd.mdx)) +- **Fact:** Local: `pnpm qa` via [`../../scripts/run-qa.mjs`](../../scripts/run-qa.mjs) — checktypes → lint → generate + drift → build → unit → e2e (`SKIP_BUILD=1`) +- **Fact:** Vercel git deploys web/api/docu ([vercel.mdx](../../apps/docu/content/docs/deployment/vercel.mdx)). CI does **not** deploy. Preview migrate gated unless `RUN_PG_MIGRATE=true`. +- **Fact:** `web-e2e` chat project runs only when a non-placeholder Anthropic key is present (`hasRealAnthropicKey()`: empty, `sk-ant-xxx`, and `sk-ant-dummy*` omit it); auth/dashboard E2E still run on forks +- **Fact:** R0 is documentation alignment. Basilic **distribution** uses GitHub Releases + npm after a maintainer merges the Release Please PR. Preview deploys still run from git as usual. +- **Fact:** npm tarball identity is SHA-256 of the packed file (see [PIPELINES.md](PIPELINES.md)). Vercel and EAS still rebuild from git. + ## Minimum Useful Artifact - intent, owner, visible state: issue or PR -- plan and acceptance criteria: `/plan-feature` for non-trivial work +- plan and acceptance criteria: `/b-plan-feature` for non-trivial work - actors: human, agent, CI, CodeRabbit - gates: product, security, destructive — ask a human -- validation: Product Ready for adopter bar; CI for Pipelines; learning: `/retro` and durable files when decisions changed - -## Recipe - -1. Inspect issues, PRs, branch, CI, and any plan. -2. Understand the actor for this step. -3. Identify missing plan, missing owner, approval only in chat. -4. Propose before implementing on non-trivial work. Surface assumptions. -5. Implement in small, reviewable chunks. Keep state in the issue or PR. -6. Hand off to review with enough context (`/code-review`). -7. Run validation through existing pipelines. Fix or escalate. -8. Obtain approval for consequential changes. Release. Capture learning in files. - -## Validation - -- Work state is visible without asking in chat. -- Handoffs include enough context for the next actor. -- Consequential decisions are in [PRODUCT.md](PRODUCT.md), `apps/docu`, or ADRs — not only merged code. -- Failed validation routes to a clear owner and next action. - -## Definition of Done - -The change moved through an explicit path. State is updated. Durable context reflects what was decided. The next actor can continue without reconstruction. - -## Agent Prompt - -Apply Workflow First to Basilic. Use `/f-workflow` plus basilic-skills playbooks. - -Read current issues/PRs, ai-workflow MDX, and `.agents/skills/workflow/` before acting. Do not rely on chat as the system of record. - -Propose before implementing on non-trivial work. Implement in reviewable chunks. Use `/git-commit` and `/git-create-pr`. Never `--no-verify`. Use the default global git user. - -Stop and ask a human for product scope, security-sensitive changes, and destructive operations. Update issues, PRs, and documentation as work progresses. Preserve intentional existing process. +- validation: Product Ready for adopter bar; CI for Workflow; learning: `/b-retro` and durable files when decisions changed +- local mirror: `pnpm qa`, `pnpm lint`, `pnpm checktypes` +- failures: `gh pr checks` / `gh run view` (`/b-fix-github-actions`); never GitHub MCP for Actions logs ## Notes -**Workflow vs Pipelines:** Workflow is how actors respond. Pipelines are the automated format, test, build, and deploy mechanics. - -**Workflow vs Documentation:** Workflow determines when context is created. Documentation preserves it. +Quality names the bar. Workflow runs it. Architecture defines deployment units. Operations runs what arrived. Never `--no-verify`. A green local sandbox is not GitHub Actions. -**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/WORKFLOW.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/WORKFLOW.md) · [Factory map](../ABOUT.md) · [AI workflow](../../apps/docu/content/docs/development/ai-workflow.mdx) +**Navigation:** [Generic spec](https://github.com/blockmatic/first/blob/main/_first/principles/WORKFLOW.md) · [Human essay](https://github.com/blockmatic/first/blob/main/_first/articles/WORKFLOW.md) · [Factory map](../ABOUT.md) · [AI workflow](../../apps/docu/content/docs/development/ai-workflow.mdx) · [GitHub Actions](../../apps/docu/content/docs/deployment/github-actions.mdx) diff --git a/_first/templates/API.md b/_first/templates/API.md deleted file mode 100644 index 66465145..00000000 --- a/_first/templates/API.md +++ /dev/null @@ -1,20 +0,0 @@ -# API First - -## Principle - -See `/f-api`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/ARCHITECTURE.md b/_first/templates/ARCHITECTURE.md deleted file mode 100644 index ad40cfa1..00000000 --- a/_first/templates/ARCHITECTURE.md +++ /dev/null @@ -1,20 +0,0 @@ -# Architecture First - -## Principle - -See `/f-architecture`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/DATA.md b/_first/templates/DATA.md deleted file mode 100644 index f67a779b..00000000 --- a/_first/templates/DATA.md +++ /dev/null @@ -1,20 +0,0 @@ -# Data First - -## Principle - -See `/f-analyst`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/DESIGN.md b/_first/templates/DESIGN.md deleted file mode 100644 index 7cf0e974..00000000 --- a/_first/templates/DESIGN.md +++ /dev/null @@ -1,20 +0,0 @@ -# Design First - -## Principle - -See `/f-designer`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/DOCUMENTATION.md b/_first/templates/DOCUMENTATION.md deleted file mode 100644 index 1eecaf6b..00000000 --- a/_first/templates/DOCUMENTATION.md +++ /dev/null @@ -1,20 +0,0 @@ -# Documentation First - -## Principle - -See `/f-info-architect`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/JOURNEYS.md b/_first/templates/JOURNEYS.md deleted file mode 100644 index fde2b1ae..00000000 --- a/_first/templates/JOURNEYS.md +++ /dev/null @@ -1,20 +0,0 @@ -# Journeys First - -## Principle - -See `/f-journeys`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/OPERATIONS.md b/_first/templates/OPERATIONS.md deleted file mode 100644 index 6e899c0c..00000000 --- a/_first/templates/OPERATIONS.md +++ /dev/null @@ -1,20 +0,0 @@ -# Operations First - -## Principle - -See `/f-operations`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/PIPELINES.md b/_first/templates/PIPELINES.md deleted file mode 100644 index 2348ff29..00000000 --- a/_first/templates/PIPELINES.md +++ /dev/null @@ -1,20 +0,0 @@ -# Pipelines First - -## Principle - -See `/f-pipelines`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/PRODUCT.md b/_first/templates/PRODUCT.md deleted file mode 100644 index 52e5be63..00000000 --- a/_first/templates/PRODUCT.md +++ /dev/null @@ -1,32 +0,0 @@ -# Product First - -## Principle - -See `/f-product`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - -## Brief - -- - -## Feature map - -- - -## Roadmap - -- - diff --git a/_first/templates/QUALITY.md b/_first/templates/QUALITY.md deleted file mode 100644 index a4d78515..00000000 --- a/_first/templates/QUALITY.md +++ /dev/null @@ -1,20 +0,0 @@ -# Quality First - -## Principle - -See `/f-quality`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/SECURITY.md b/_first/templates/SECURITY.md deleted file mode 100644 index 898d425f..00000000 --- a/_first/templates/SECURITY.md +++ /dev/null @@ -1,20 +0,0 @@ -# Security First - -## Principle - -See `/f-security`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/_first/templates/WORKFLOW.md b/_first/templates/WORKFLOW.md deleted file mode 100644 index 9e6c08c7..00000000 --- a/_first/templates/WORKFLOW.md +++ /dev/null @@ -1,20 +0,0 @@ -# Workflow First - -## Principle - -See `/f-workflow`. - -## Artifacts - -- **Fact:** -- **Drift:** -- **Unresolved:** - -## Minimum Useful Artifact - -- - -## Notes - -- - diff --git a/apps/docu/README.md b/apps/docu/README.md index dd66dd46..51236fc5 100644 --- a/apps/docu/README.md +++ b/apps/docu/README.md @@ -14,7 +14,8 @@ Content is `content/docs/` (`architecture`, `development`, `testing`, `deploymen - [Getting Started](content/docs/development/index.mdx) - [Product Ready](content/docs/testing/product-ready.mdx) -- [AI Workflow](content/docs/development/ai-workflow.mdx) +- [AI Workflow](content/docs/development/ai-workflow.mdx) — `/b` catalog, `/b-*` playbooks, checklists, and migration - [Architecture](content/docs/architecture/index.mdx) - [Security](content/docs/architecture/security.mdx) - [Deployment](content/docs/deployment/index.mdx) +- [Scaffolding and Releases](content/docs/adrs/012-scaffolding-and-releases.mdx) diff --git a/apps/docu/content/docs/adrs/012-scaffolding-and-releases.mdx b/apps/docu/content/docs/adrs/012-scaffolding-and-releases.mdx new file mode 100644 index 00000000..0605e7f2 --- /dev/null +++ b/apps/docu/content/docs/adrs/012-scaffolding-and-releases.mdx @@ -0,0 +1,160 @@ +--- +title: "ADR 012: Scaffolding and Releases" +description: "Accepted Basilic generator, template boundaries, conventional-commit versioning, and AI-assisted releases." +--- + +Status: **Accepted.** Implementation landed September 5, 2026. Human gates (npm trusted publisher, GitHub App, squash settings, Product Ready from the published package before announcing 1.0.0) remain maintainer-owned. + +## Recommendation + +Ship **`create-basilic`** from **`tools/create-basilic`**. Generate an independent repository containing API, web, mobile, shared packages, and development tooling; exclude the documentation application and the generator. Bundle a tested template inside each published generator version. Use **Release Please** to propose versions and changelogs from conventional commits, with a maintainer merging the release PR to authorize publication. AI reviews release impact and drafts explanations; deterministic rules calculate versions. + +Use the existing `tools/` directory. Adding `tooling/` would duplicate a boundary already represented in `pnpm-workspace.yaml`. + +### Assumptions + +- “MPX … init” means an `npx`-style project creation command. The shipped interface is `npx create-basilic@latest my-app`. +- “Releasing Emberation” means Basilic release/version automation. No separate product named Emberation is assumed. +- “Exclude the CLI itself” means the new generator. The existing API client CLI remains part of the starter. +- Version one delivers the complete starter, with no app picker or automatic upgrades of existing projects. + +## Current repository facts + +| Evidence | Implication | +| --- | --- | +| `pnpm-workspace.yaml` includes `apps/*`, `packages/*`, `tools/*` | Reuse the workspace layout. | +| `packages/cli` is private `@repo/cli`, with a `basilic` executable | A generator called `basilic` would collide with the API CLI. | +| `tools/eslint` and `tools/typescript` provide workspace dependencies | Copying only `apps/`, or excluding all `tools/`, produces an incomplete project. | +| Root README and After fork describe generator-first adoption; clone and GitHub template remain for contributors | New products use `create-basilic`. | +| Publishing documentation describes package packing and manual publishing | Packing support exists, but is not an automated release policy. | +| Checked-in workflows cover quality, security, mobile, scaffold classification, Release Please, and npm publish | Conventional PR titles are gated. Release Please is upstream-only. | + +## Naming and component boundaries + +| Component | Location / name | Responsibility | +| --- | --- | --- | +| Scaffold generator | `tools/create-basilic`; npm `create-basilic`; bin `create-basilic` | Create a new project. Never included in its output. | +| API CLI | Keep `packages/cli`, `@repo/cli`, bin `basilic` initially | Call the generated project's API through `@repo/core`. | +| Template assembly | Inside `tools/create-basilic` | Select files, apply explicit transforms, validate, and bundle the template. | +| Release automation | Upstream `.github/workflows/` and release configuration | Version and publish Basilic; excluded from generated projects. | + +Preferred command: `npx create-basilic@latest my-app`; pnpm equivalent: `pnpm dlx create-basilic@latest my-app`. The positional directory is sufficient; no redundant `init` subcommand is needed. An optional alias can be added later. Exact-version usage, for example `npx create-basilic@1.2.3 my-app`, selects that version's bundled template; this is an illustrative version. + +Check npm ownership before implementation. If the unscoped name is unavailable, use an owned scope such as `@blockmatic/create-basilic` **only after verifying ownership**. Neither name has been reserved or verified here. Avoid publishing multiple aliases initially. + +Keep the API CLI's internal package name to avoid changing Turbo filters and generation scripts. Describe it consistently as “API CLI” and the new package as “project generator.” A later rename of the API binary must also address its help text, `BASILIC_API_KEY`, config directory, documentation, and compatibility; a folder rename alone would not resolve those identities. + +## Define what gets generated + +Maintain one version-controlled **include-and-transform manifest**, colocated with the generator. Assemble from a clean source revision, not a developer's working directory. Fail CI when a newly tracked path has no classification: included, transformed, or excluded. This prevents future maintainer tooling from silently entering templates. + +| Source | Initial policy | +| --- | --- | +| `apps/api`, `apps/web`, `apps/mobile` | Include source, tests, assets, migrations, and safe configuration. | +| `apps/docu` | Exclude the application, its build dependencies, and its Turbo tasks. | +| Existing `packages/*`, including `packages/cli` | Include. Preserve `@repo/*` names and `workspace:*` links. | +| `tools/eslint`, `tools/typescript` | Include; generated projects need these. | +| `tools/create-basilic` and future maintainer-only tools | Exclude. | +| Root package/workspace/Turbo/lint configuration and `scripts/` | Include required files; remove references to excluded components. | +| Lockfile | Produce and validate a lockfile for the assembled workspace using pinned pnpm. Do not hand-edit YAML dependency resolutions. | +| `.github` | Include portable quality workflows and their local actions; omit Basilic publication workflows, release state, and maintainer ownership metadata. Deployment workflows require adopter configuration and must not target upstream projects. | +| `.cursor`, `.agents`, `AGENTS.md`, safe `.deepsec` configuration | Retain the shared workflow; replace upstream project bindings and repair documentation pointers. | +| FIRST user pack | Retain reusable guidance; create an unfilled adopter instance only for stations listed in `FIRST.md`; retarget `FIRST.md` and agent pointers. Do not copy `_first/basilic` or fabricate product decisions. | +| `LICENSE` | Preserve MIT copyright and license text. | +| Git history, caches, dependency installs, build output, credentials, local environment files | Exclude. Include approved `.env..example` files and audited non-secret defaults. | + +The FIRST reset is an additional recommendation beyond the two application exclusions: `_first/FIRST.md` explicitly says not to copy `basilic/` out. Generated projects should not inherit Basilic's roadmap as their own. + +**Documentation needs special treatment.** Existing agent rules require local files under `apps/docu/content/docs`. Excluding that whole tree without repairing those references would break the development workflow. Recommended v1: derive a lightweight `docs/basilic/` reference snapshot from the canonical technical MDX during assembly, strip site-only presentation syntax, and rewrite generated pointers to it. It has no documentation app, dependencies, or build. Keep links to hosted docs for browsing. The snapshot is generated output, not a second upstream authoring source. Acceptance tests must verify every mandatory agent read resolves. If absolutely no documentation content should ship, revise the generated instructions to use remote references explicitly and accept the network dependency. + +### Explicit transforms + +Change root project name and write an adopter README with setup steps, customization guidance, and upstream references. Keep internal package names stable. Use an audited mapping for app display names and optional mobile identifiers; never globally replace `basilic` or `blockmatic`. Preserve upstream links, license notices, API compatibility names, and generated source contracts deliberately. + +Remove upstream MCP/project bindings, EAS project IDs, deployment ownership, and repository-specific security-review URLs. Preserve safe example placeholders. Rewrite scripts, CI filters, Turbo tasks, and documentation links that target excluded files. Reuse the documented OpenAPI generator when contracts change; do not edit generated clients or migration SQL. + +## Generation behavior and artifact design + +1. Validate project name, destination, and supported Node version before writing. Initial baseline follows the repository's Node 24 engine and exact `packageManager` version. +2. Refuse non-empty destinations. Noninteractive `--yes` accepts safe defaults, never overwrites. Support paths containing spaces and clear exit codes. +3. Copy the bundled template into a temporary sibling directory. Prevent path traversal and escaping symlinks; apply typed, targeted transforms. +4. Check workspace dependencies, required files, and prohibited output. Record generator version, template source SHA, template digest, and selected inputs under a `basilic` field in root `package.json`; this is provenance, not a new configuration framework. +5. Move the completed tree to the destination atomically where supported. On failure, clean only temporary files created by this run. +6. Print the destination and existing setup sequence: `pnpm setup`, database start, `pnpm reset`, then `pnpm dev`, with a link to Product Ready. Do not automatically reset databases, create remotes, publish, deploy, or commit. Installation/setup remains an explicit next step in v1. + +Bundle compiled generator code and the sanitized template in **one npm tarball**. The running generator must not depend on unresolved workspace packages, Git, GitHub API availability, or downloading `main`. Use a package `files` allowlist covering the executable and template; the existing package pack helper's `files: ["dist"]` policy cannot be reused blindly. Test the actual tarball outside the monorepo. + +Tradeoff: the package is larger, and template changes require publishing a generator release. This is preferable initially to operating separate CLI/template versions and download verification. Revisit separate immutable template artifacts only if package size or independent release cadence becomes a measured problem. Exact-version generation reproduces source output, not external services or mutable dependency registries. + +## Versioning policy and tool choice + +Use one **Basilic distribution version** for the generator and its bundled template. Shared starter packages keep their existing independent metadata; this proposal does not publish every `@repo/*` package. Generated products start with their own version, independent of Basilic provenance. + +Release Please fits the existing conventional-commit workflow and prepares reviewable release PRs. Changesets is a good alternative when multiple public packages need coordinated dependency bumps, but would add separate release-intent files here. Reconsider it when independently published SDK packages become an actual requirement. See [Release Please](https://github.com/googleapis/release-please) and [Changesets](https://github.com/changesets/changesets). + +Configure a **root release component**, so changes in copied apps, packages, tools, and scripts count toward the distribution. Synchronize its version into `tools/create-basilic/package.json` through Release Please's configured extra-file support. Scoping release detection only to the generator directory would miss template updates. Pin and test the configuration, including manifest state, changelog, tag format `vX.Y.Z`, bootstrap SHA, and exclusion rules. + +| Change | Proposed release impact | +| --- | --- | +| `fix`: generator bug, template defect, security/dependency correction | Patch. | +| `feat`: backward-compatible generator option or starter capability | Minor. | +| `!` / `BREAKING CHANGE`: removed option, changed default contract, raised runtime floor, incompatible starter contract | Major once stable. | +| Docs-only, internal tests, maintenance with no shipped effect | No release by default. | + +Classify dependency upgrades by impact, not the word “deps.” A copied rule/docs change that fixes generated behavior deserves a release-bearing commit. CI should flag payload changes hidden behind non-releasing commit types and require a correction or an explicit reviewed explanation. Conventional syntax cannot prove semantic compatibility. + +Recommend stable `1.0.0` only after the acceptance gates below pass; use a manual `0.1.0-next.1` preview if needed before then. Configure pre-1.0 breaking-change behavior explicitly and test it. Keep preview publishing manual initially; exclude prereleases from npm `latest` and GitHub's latest stable release. Do not derive a bootstrap version from every existing workspace's `0.0.0` or `0.1.0` value. + +Require conventional PR titles, squash merges, and preservation of breaking-change footers in the final commit message. Set an explicit initial baseline so historical commits do not unexpectedly determine the first release. Routine releases batch automatically in one open release PR; merging that PR is the release decision. + +## Automated release path + +1. **Feature PR:** run current relevant checks plus generator tests, template classification, and conventional-title validation. Keep the release check always reportable; do not let path filters leave required checks pending. +2. **Merge to main:** Release Please updates one release PR containing the proposed version and changelog. AI may add a separate review summary, which must not be overwritten by bot regeneration. +3. **Release PR:** run the full scaffold acceptance checks against its exact content. A maintainer reviews the version, migration notes, and results, then merges. +4. **Release commit:** check out the exact release SHA; assemble and pack once, record SHA-256, and test that tarball. Do not rebuild during publication. Serialize releases and retain the artifact for retry. +5. **Publish:** publish the validated tarball to npm with a configured trusted publisher; attach the same artifact/checksum to the matching GitHub Release. Verify public installation by exact version and verify provenance. Announce completion only after both distribution surfaces are verified. + +Release Please creates a GitHub Release when the release PR merges; it does not make npm publication transactional. Mark a release as pending distribution verification until the publish check succeeds. A GitHub Release alone is not success. + +Use a repository-scoped GitHub App credential for the release bot if downstream PR/tag events must run checks. The default `GITHUB_TOKEN` does not trigger ordinary follow-on workflows for bot-created resources; explicitly test this trigger chain. Keep publication separate from PR execution. See the [Release Please Action credential guidance](https://github.com/googleapis/release-please-action#github-credentials). + +Prefer npm OIDC trusted publishing on a supported GitHub-hosted runner, with the exact repository, workflow, and environment configured in npm. Give only the publishing job `id-token: write`; retain normal `pnpm` workspace commands and verify that the pinned publishing client supports OIDC before rollout. npm documents current client requirements and automatic provenance for supported public repositories in [Trusted publishing](https://docs.npmjs.com/trusted-publishers/). Package ownership, initial publication/bootstrap, GitHub App installation, and protected environment setup are one-time maintainer tasks; do not assume an unpublished package is already configured. + +Pin Actions to reviewed commit SHAs. Verify release-tag ancestry and artifact identity on manual retries. If npm succeeded but GitHub asset upload failed, repair the release using the retained artifact; never attempt to overwrite an existing npm version. For a defective release, publish a patch, deprecate the affected version where appropriate, and move `latest` to a verified version if necessary. Never rewrite published tags or promise to undo already generated projects. Document emergency publication recovery with the normal checks intact. + +This lifecycle governs **Basilic distribution**, separately from existing Vercel/EAS app deployments. Generated repositories do not receive Basilic's npm credentials, release bot, or release state. Adopter release management can be an explicit later option. + +## AI assistance with bounded authority + +Extend the existing Cursor-first review workflow before introducing another agent service. At PR time, ask AI to compare the diff with public CLI/template contracts and suggest a conventional title, release impact, migration notes, and evidence paths. Run automatic analysis on every relevant PR if desired, but label the result advisory. + +At release-PR time, provide merged PRs since the previous tag and test results. Ask for concise user-facing improvements, breaking changes, required actions, and known limitations, with links to each supporting PR. Keep the deterministic changelog; publish reviewed AI prose as supplemental release notes. Cache analysis by head SHA, bound cost/time, and fall back to deterministic notes if the model fails. + +AI must not choose an unreviewed major bump, edit authoritative version files independently of Release Please, merge releases, or possess publication credentials. Treat PR text and source comments as untrusted input. Separate analysis from the narrowly authorized bot that posts results. Human review resolves disagreement between the diff, commit classification, and suggested release impact. No new recurring agent service or external posting is enabled by this document. + +## Implementation sequence and acceptance + +| Stage | Deliverable | Exit evidence | +| --- | --- | --- | +| 1. Contract | Final package identity, manifest, documentation snapshot policy, release baseline | Every tracked path classified; all generated agent pointers resolvable. | +| 2. Generator | `tools/create-basilic`, assembly transforms, packaged executable | Tarball runs outside the workspace; exact-version fixture output is stable. | +| 3. Generated-project checks | Dedicated CI coverage and portable defaults | Fresh install with frozen lockfile, types, lint, build, unit tests, OpenAPI drift, and API/web E2E pass without docu or generator. | +| 4. Release automation | Release Please config, release/publish workflow, recovery instructions | Dry run proves version propagation, bot triggers, tarball identity, and retry behavior. Controlled preview installs successfully. | +| 5. Launch | Stable release and onboarding updates | Product Ready exercised from the published package; docs accurately describe shipped behavior. | + +Test invalid names, existing/non-empty paths, paths with spaces, interruption, permission failures, traversal/symlink handling, no-network template copying, and execution from outside Git. Test generation on Linux, macOS, and Windows; distinguish generator support from platform requirements of the full development stack. + +Scan packed and generated files for secrets and forbidden paths. Verify workspace dependency closure, portable workflows, license retention, excluded Turbo targets, and lockfile consistency. Reuse root `pnpm qa` for the generated workspace once its transforms are complete, plus `pnpm --filter @repo/docu checktypes` for upstream documentation. Add generator/fixture scripts to package manifests before advertising new validation commands. + +At launch, update root and generator READMEs, Getting Started, After fork, CLI, Publishing, GitHub Actions, and Monorepo documentation together. Update `_first/basilic/PRODUCT.md` with the delivered adoption capability and the architecture/workflow overlays with verified implementation facts. Keep forks documented for upstream contributions; new products use the generator. Existing copies remain owner-managed: offer changelogs and migration guidance before considering a future upgrade command. + +**Maintainer gates still open:** claim npm `create-basilic` (or an owned scope) and bind trusted publishing; install the GitHub App and protected `release` environment; squash-merge with `PR_TITLE` + `PR_BODY`; authorize who may merge release PRs; announce **1.0.0** only after Product Ready from `npx create-basilic@`. + +## References + +- Repository constraints: root `AGENTS.md`; `.cursor/rules/base/{general,file-organization,git,github-actions,naming,docs,readme}.mdc`. +- FIRST: `_first/{AGENTS,ABOUT,FIRST}.md`; `_first/basilic/{ARCHITECTURE,WORKFLOW,PRODUCT}.md`; `.agents/skills/f/f-architecture/SKILL.md`, `.agents/skills/f/f-workflow/SKILL.md`, and their analyst/spec references. +- Local evidence: root `README.md`, `package.json`, `pnpm-workspace.yaml`, `turbo.json`; `packages/cli/{README.md,package.json}`; `tools/{eslint,typescript}`; `.github/workflows/`. +- Existing technical guidance: [Monorepo](/docs/architecture/monorepo), [After fork](/docs/development/after-fork), [AI workflow](/docs/development/ai-workflow), [Publishing](/docs/deployment/publishing), [GitHub Actions](/docs/deployment/github-actions), [Product Ready](/docs/testing/product-ready). +- Tool references: [Release Please manifest configuration](https://github.com/googleapis/release-please/blob/main/docs/manifest-releaser.md), [Release Please Action](https://github.com/googleapis/release-please-action), [Changesets](https://github.com/changesets/changesets), [npm trusted publishing](https://docs.npmjs.com/trusted-publishers/). Consulted September 5, 2026; pin and verify selected versions during implementation. diff --git a/apps/docu/content/docs/adrs/index.mdx b/apps/docu/content/docs/adrs/index.mdx index 94ff1cc2..a5591bce 100644 --- a/apps/docu/content/docs/adrs/index.mdx +++ b/apps/docu/content/docs/adrs/index.mdx @@ -47,6 +47,8 @@ Product usage and observability: ## Deployment & CI/CD +- **[ADR 012: Scaffolding and Releases](/docs/adrs/012-scaffolding-and-releases)** - `create-basilic` generator, template boundaries, Release Please, trusted npm publish + Mobile app build and preview workflows: -- **[ADR 010: Expo CI/CD](/docs/adrs/010-expo-cicd)** - EAS builds, preview distribution, PR OTA updates (Android-only v1) \ No newline at end of file +- **[ADR 010: Expo CI/CD](/docs/adrs/010-expo-cicd)** - EAS builds, preview distribution, PR OTA updates (Android-only v1) diff --git a/apps/docu/content/docs/adrs/meta.json b/apps/docu/content/docs/adrs/meta.json index d13972bd..fc6f161a 100644 --- a/apps/docu/content/docs/adrs/meta.json +++ b/apps/docu/content/docs/adrs/meta.json @@ -12,6 +12,7 @@ "008-database", "009-api-architecture", "010-expo-cicd", - "011-product-analytics" + "011-product-analytics", + "012-scaffolding-and-releases" ] } diff --git a/apps/docu/content/docs/architecture/monorepo.mdx b/apps/docu/content/docs/architecture/monorepo.mdx index 0e1c3a73..d4c2933b 100644 --- a/apps/docu/content/docs/architecture/monorepo.mdx +++ b/apps/docu/content/docs/architecture/monorepo.mdx @@ -21,7 +21,13 @@ flowchart TB API["api — Fastify 5"] Web["web — Next.js 16"] Mobile["mobile — Expo 57"] - Docu["docu — Fumadocs"] + Docu["docu — Fumadocs (upstream only)"] + end + + subgraph Tools["tools/"] + Eslint["eslint"] + Ts["typescript"] + Create["create-basilic (upstream only)"] end API -->|OpenAPI| Core @@ -39,7 +45,9 @@ flowchart TB Cli -->|HTTP| API ``` -**Apps:** `api` (Fastify), `web` (Next.js 16), `mobile` (Expo 57 UI scaffold — shares **tokens** later, not components/`@repo/core` types yet), `docu` (Fumadocs). +**Apps:** `api` (Fastify), `web` (Next.js 16), `mobile` (Expo 57 UI scaffold — shares **tokens** later, not components/`@repo/core` types yet), `docu` (Fumadocs, not copied into generated projects). + +**Tools:** `eslint`, `typescript` (copied), `create-basilic` (generator; excluded from generated projects). See [ADR 012](/docs/adrs/012-scaffolding-and-releases). **Packages:** `core` (generated, no app runtime), `react` (handwritten hooks on core), `ui`, `email`, `error`, `utils`, `cli`. diff --git a/apps/docu/content/docs/deployment/github-actions.mdx b/apps/docu/content/docs/deployment/github-actions.mdx index 766fedb0..ab1433b7 100644 --- a/apps/docu/content/docs/deployment/github-actions.mdx +++ b/apps/docu/content/docs/deployment/github-actions.mdx @@ -7,7 +7,7 @@ GitHub Actions workflows automate quality checks on pull requests (opened and up ## Workflows -Path filters are used only for test workflows (app E2E and package unit tests) so they run when relevant code changes. Lint and `security.yml` run on every PR. DeepSec runs on same-repo PRs from OWNER, MEMBER, or COLLABORATOR. Path patterns are defined in each workflow file. +Path filters are used only for test workflows (app E2E and package unit tests) so they run when relevant code changes. Lint, `security.yml`, conventional **PR title**, **template classification**, **generator tests**, and **release-impact** run on every PR (always reportable; they do not use path filters that leave required checks pending). DeepSec runs on same-repo PRs from OWNER, MEMBER, or COLLABORATOR. Path patterns are defined in each workflow file. ### Web (`web-e2e.yml`) @@ -50,6 +50,21 @@ gh run view --log-failed Linting and type checking on every PR (`pnpm lint`, then `pnpm exec turbo run checktypes`). Supports `workflow_dispatch` for manual runs. Sets `TURBO_TOKEN` / `TURBO_TEAM` when configured. +### Scaffold (`pr-title.yml`, `scaffold.yml`, `release-impact.yml`, `scaffold-acceptance.yml`) + +Always report on pull requests: + +- **PR title** — conventional types (`feat`, `fix`, `perf`, …). +- **Template classification** — every `git ls-files` path is include / transform / exclude. +- **Generator tests** — `create-basilic` unit tests on Ubuntu, macOS, and Windows (copy/generate). Full-stack Product Ready remains Unix-oriented. +- **Release impact** — payload path changes behind `docs`/`chore`/`test`/`ci`/`style` titles fail unless the body has `skip-release: true`. + +**Generated tree QA** (`scaffold-acceptance.yml`) runs the assembled template’s `pnpm qa` (types, lint, OpenAPI drift, build, unit, API/web E2E) on **release-please PRs** and `workflow_dispatch`. Feature PRs skip that job with success so the required check is not pending. + +### Release Please and publish + +Upstream only (excluded from generated trees): `release-please.yml` on `main` (GitHub App token), `publish-create-basilic.yml` on `v*` tags (OIDC npm publish + GitHub Release asset). See [Publishing](/docs/deployment/publishing). + ### Mobile (`mobile-build.yml`, `mobile-preview.yml`, `mobile-pr-preview.yml`) EAS builds and PR OTA previews for the Expo app. Requires `EXPO_TOKEN` in GitHub Secrets. v1: Android-only, no store submission. diff --git a/apps/docu/content/docs/deployment/publishing.mdx b/apps/docu/content/docs/deployment/publishing.mdx index c9330517..2d322a8b 100644 --- a/apps/docu/content/docs/deployment/publishing.mdx +++ b/apps/docu/content/docs/deployment/publishing.mdx @@ -3,7 +3,28 @@ title: "Publishing Packages" description: "How to publish monorepo packages to npm using the dual-mode export strategy." --- -Packages use **dual-mode exports**. **Pattern A** (`@repo/core`, `@repo/react`, `@repo/ui`): workspace `import` is `src/`; `prepack` builds and rewrites to `dist/` for npm. **Pattern B** (`@repo/utils`, `@repo/error`, `@repo/email`): workspace Node already uses `dist/`; `prepack` still rewrites remaining `src/` paths (`types`/`source`) and packs `files: ["dist"]`. ESM details: [ESM & TypeScript Strategy](/docs/architecture/esm-strategy). +## Basilic distribution (`create-basilic`) + +The public npm package is **`create-basilic`**, built from `tools/create-basilic`. Do **not** run `scripts/prepare-publish.mjs` for it (`files: ["dist"]` would drop the bundled template). + +1. Release Please opens a release PR from conventional commits (root version + `tools/create-basilic/package.json`). +2. Maintainers merge that PR after scaffold acceptance. The tag is `vX.Y.Z`. +3. `publish-create-basilic.yml` checks out that SHA, assembles and packs **once**, tests the tarball, publishes with npm trusted publishing (`id-token` only on that job), and attaches the same tarball + SHA-256 to the GitHub Release. +4. Prerelease versions (`0.1.0-next.1`) use npm dist-tag `next`, not `latest`. + +### Maintainer setup (one-time) + +- Claim npm `create-basilic` (or an owned scope) and bind a trusted publisher to this repo, `publish-create-basilic.yml`, and the `release` environment. +- Install a GitHub App for Release Please (`RELEASE_PLEASE_APP_ID` / `RELEASE_PLEASE_APP_PRIVATE_KEY`) so follow-on checks run. Default `GITHUB_TOKEN` does not chain. +- Protect the `release` environment. Squash-merge with `PR_TITLE` + `PR_BODY`. Authorize who may merge release PRs. + +### Recovery + +Defective release → patch + deprecate; never rewrite tags; never promise to un-generate adopter repos. If npm succeeded and the GitHub asset failed, re-attach the retained artifact. Never overwrite an npm version. + +## Dual-mode `@repo/*` packing + +Packages use **dual-mode exports**. **Pattern A** (`@repo/core`, `@repo/react`, `@repo/ui`): workspace `import` is `src/`; `prepack` builds and rewrites to `dist/` for npm. **Pattern B** (`@repo/utils`, `@repo/error`, `@repo/email`): workspace Node already uses `dist/`; `prepack` still rewrites remaining `src/` paths (`types`/`source`) and packs `files: ["dist"]`. ESM details: [ESM & TypeScript Strategy](/docs/architecture/esm-strategy). This path is **not** how `create-basilic` is published. v1 does not publish every `@repo/*` package. ## How It Works diff --git a/apps/docu/content/docs/development/after-fork.mdx b/apps/docu/content/docs/development/after-fork.mdx index 4590ca91..7b3a7b8b 100644 --- a/apps/docu/content/docs/development/after-fork.mdx +++ b/apps/docu/content/docs/development/after-fork.mdx @@ -3,13 +3,14 @@ title: After fork description: Template vs fork, Product Ready, and what to replace when the copy is your product. --- -Basilic stays the starter brand. This page is for after GitHub **Use this template** or a fork. First run is still [Product Ready](/docs/testing/product-ready). There is no rename script. +Basilic stays the starter brand. **New products** use `npx create-basilic@latest my-app`. This page is also for GitHub **Use this template** or a fork. First run is still [Product Ready](/docs/testing/product-ready). There is no rename script and no upgrade CLI. Enable **Template repository** in GitHub settings so **Use this template** appears. Maintainers do that; agents cannot. Clone still works without the flag. -## Template vs fork +## Generator vs template vs fork -- **Use this template** — start a product. New history, no obligation to merge upstream. +- **`npx create-basilic@latest`** — start a product. Independent git history; no docu app; no generator; local docs snapshot in `docs/basilic/`. +- **Use this template** — start a product from GitHub. Includes the documentation app and generator until you remove them. - **Fork** — contribute back to Basilic. Keep the upstream remote. ## Run @@ -32,7 +33,9 @@ Do not sweep every `blockmatic` docs URL. Those point at the upstream starter. ## FIRST -`_first/basilic/` is this starter’s filled instance. Keep it as the example, or copy [`_first/templates/`](https://github.com/blockmatic/basilic/tree/main/_first/templates) to `_first//` and retarget [`_first/FIRST.md`](https://github.com/blockmatic/basilic/blob/main/_first/FIRST.md). Do not add a 13th station. Specs stay `/f-*` from `npx skills add blockmatic/first`. +Generated projects get an **unfilled** `_first/PRODUCT.md` and a retargeted `_first/FIRST.md`. Do not copy `_first/basilic/` out — that overlay is this starter’s filled instance. + +In this repository, `_first/basilic/` stays the Basilic adoption pack. Specs stay `/f-*` from `npx skills add blockmatic/first`. Absent files beat empty stubs. ## CI secrets diff --git a/apps/docu/content/docs/development/ai-workflow.mdx b/apps/docu/content/docs/development/ai-workflow.mdx index 9cc9ca20..b4d8f368 100644 --- a/apps/docu/content/docs/development/ai-workflow.mdx +++ b/apps/docu/content/docs/development/ai-workflow.mdx @@ -7,11 +7,11 @@ Development is **Cursor-first**. Claude Code follows the same rules and skills v Judgment lives in planning and review. Implementation is incremental. See [Engineering in the AI Era](https://gaboesquivel.com/blog/2026-02-engineering-ai-era). Index this site once — [Cursor Setup](/docs/development/cursor-setup). Versioning and how to update skills: [Cursor Skills](/docs/development/cursor-skills). -FIRST is vendored from [`blockmatic/first`](https://github.com/blockmatic/first). Install `npx skills add blockmatic/first`. Copy `README.md`, `AGENTS.md`, `ABOUT.md`, and `templates/` into `_first/`; keep `FIRST.md` and `_first/basilic/` as this repo’s instance. Essays live on the FIRST site. Agents load `_first/AGENTS.md`, then `_first/ABOUT.md`, then `_first/FIRST.md`, then this repository's instructions and skills, then `/f-*` for the station in scope, then the instance listed in FIRST.md. FIRST does not replace this repository's Cursor-first workflow. Factory validation (`pnpm validate`) runs in the first repo. Two catalogs: `blockmatic/first` (`/f-*`) and `blockmatic/basilic-skills` (tech + `/workflow`). +FIRST is vendored from [`blockmatic/first`](https://github.com/blockmatic/first). Install `npx skills add blockmatic/first`. Copy `README.md`, `AGENTS.md`, and `ABOUT.md` into `_first/`; keep `FIRST.md` and `_first/basilic/` as this repo’s instance. Write overlays only for stations listed as In. Essays live on the FIRST site. Agents load `_first/AGENTS.md`, then `_first/ABOUT.md`, then `_first/FIRST.md`, then this repository's instructions and skills, then `/f-*` for the station in scope, then the instance listed in FIRST.md. FIRST does not replace this repository's Cursor-first workflow. Factory validation (`pnpm validate`) runs in the first repo. Two catalogs: `blockmatic/first` (`/f-*`) and `blockmatic/basilic-skills` (tech + workflow). The `/b` dispatcher is a local preview of the catalog migration; installed playbooks live under `.agents/skills/b/` (`/b-*`, same behavior as the former `/workflow` tree). ## FIRST stations -Product, Journeys, Design, Architecture, Data, API, Documentation, Workflow, Pipelines, Quality, Security, Operations. Pick one primary station. Overlays are deltas in `_first/basilic/`. Durable product facts: `_first/basilic/PRODUCT.md`. `apps/docu` is technical docs for adopters. +Product, Journeys, Architecture, Data, API, Documentation, Workflow, Quality, Security, Operations. Pick one primary station. Overlays are deltas in `_first/basilic/`. Durable product facts: `_first/basilic/PRODUCT.md`. `apps/docu` is technical docs for adopters. ## Sources of truth @@ -22,8 +22,8 @@ Always-on rules point at `apps/docu/content/docs/` for technical topics and `_fi | Layer | Role | How it loads | | --- | --- | --- | | **Rules** (`.cursor/rules/*.mdc`) | Constraints. Override everything else | Always on: `general`, `naming`, `git`. Other rules attach when matching files are in context | -| **Workflow skills** (`.agents/skills/workflow//`) | Slash playbooks | Type `/name` | -| **FIRST skills** | `/f-*` from `npx skills add blockmatic/first` | Type `/f-product`, `/f-designer`, … | +| **Workflow skills** (`.agents/skills/b/b-/`) | Slash playbooks | Type `/b-` | +| **FIRST skills** | `/f-*` from `npx skills add blockmatic/first` | Type `/f-product`, `/f-journeys`, … | | **Tech skills** (`.agents/skills/-v/`) | Patterns for a stack | Agent loads when relevant; `@` or `/` to force | | **Indexed docs** | Advisory | Never override rules or source | @@ -34,20 +34,44 @@ In Cursor chat, type `/` then the skill name. - **Enter** — attach for this message only - **Option+Enter** (Alt+Enter) — pin for the session -Daily set: `/plan-feature`, `/exec-push`, `/git-commit`, `/code-review`, `/deslop`, `/retro`. `/` lists the rest. Names match the folder under `.agents/skills/workflow/` (`audit-accessibility`, not the old typo). +Use `/b` (local preview dispatcher) to list the catalog, `/b ` to route a task, or the full `/b-` to load a playbook directly. FIRST `/f-*` remains the separate decision catalog. -Do not `@.cursor/commands/…` — that directory is gone. +| Task | Shortcut | Direct skill | +| --- | --- | --- | +| Plan a feature | `/b plan` | `/b-plan-feature` | +| Implement and verify locally | `/b build` | `/b-build` | +| Review a change | `/b review` | `/b-code-review` | +| Diagnose or fix a failure | `/b debug` | `/b-debug-issue` | +| Run tests and fix failures | `/b test` | `/b-run-all-tests-and-fix` | +| Commit intended changes | `/b commit` | `/b-git-commit` | +| Push the intended branch | `/b push` | `/b-git-push` | +| Create a described PR | `/b pr` | `/b-git-create-pr` | +| Reflect on completed work | `/b retro` | `/b-retro` | + +An empty `/b` lists options; an unknown token reports the available names. Neither starts work. `/b-build` ends with verified local changes. `/b-exec-push` explicitly requests implementation, full validation, commit, push, and a described PR. No workflow implies merge or deployment. -Claude Code has no `/` menu. Read `.agents/skills/workflow//SKILL.md` instead. +Names match folders under `.agents/skills/b/`, such as `b-plan-feature`. This is a rename: old standalone names such as `/plan-feature` are not registered aliases. Update pinned prompts and reload discovery after installing. When a harness does not expose the slash menu, read `.agents/skills/b/SKILL.md` and the named child directly. Do not create another skills tree. ## Workflow -1. **Plan** — Plan mode, then `/plan-feature` (Grok 4.6, or Sol for large/ambiguous work). List goals first. Surface edge cases before coding. -2. **Review** — Read the plan. Check scope, dependencies, approach. `/review-plan` if you want a structured pass. -3. **Execute** — `/exec-push` (or implement in small steps). Composer 2.5 is fine for mechanical multi-file edits; stay on Grok 4.6 or Sol when judgment is still required. Test between steps. -4. **Commit** — `/git-commit`. Ship <30-minute chunks. +1. **Plan** — `/b plan` for non-trivial work. Read existing implementation and docs; list goals, assumptions, acceptance conditions, dependencies, and verification per slice. Planning alone does not create a branch or implement code. +2. **Review the plan** — `/b-review-plan` checks scope and ordering. Resolve consequential decisions through FIRST; continue routine work already authorized. +3. **Implement** — `/b build` delivers complete slices and runs affected checks. Preserve unrelated changes and generated-source ownership. Update matching docs when behavior changes. +4. **Review the change** — `/b review` returns evidenced findings and verification limits. Reviews stay read-only unless fixes are requested. +5. **Publish when requested** — `/b commit`, `/b push`, or `/b pr` perform their named actions. `/b-exec-push` combines the implementation-to-PR path and requires `pnpm qa`. +6. **Learn** — `/b retro` identifies concrete improvements; changed conventions belong in existing durable documentation. + +Tech skills (`fastify-v5`, `next-v16`, `vercel-react-v1`) load when relevant. FIRST owns product and cross-cutting decisions; the workflow does not add another backlog or quality policy. + +The generator retains this catalog and rewrites its documentation pointers to the adopter’s local docs snapshot. `/b-release-review` is advisory and preserves the release tooling’s human gates. + +## Checklists and evidence + +The core playbooks use inputs, steps, verification, and a handoff. Reviews inspect correctness, clarity, architecture, security, performance, and the verification claims. Debugging connects a reproduction to its cause and repeats the original scenario after a fix. + +Shared completion evidence lives inside the installed `b/references/` directory. Record checks as passed, failed, not run, or not applicable with a reason. A type check alone does not verify a browser interaction. Documentation changes need proportionate checks; TDD remains explicit through `/b-use-tdd` or repository requirements. -Tech skills (`fastify-v5`, `next-v16`, `vercel-react-v1`) usually attach themselves. Force with `@.agents/skills/fastify-v5` or `/fastify-v5`. +The selective adaptation of [Addy's skills](https://skills.addy.ie/) is documented in the basilic-skills checkout (not yet published on GitHub). See [Cursor Skills](/docs/development/cursor-skills#workflow-migration) for local preview and publication status. ## Models (30 Aug 2026) diff --git a/apps/docu/content/docs/development/cli.mdx b/apps/docu/content/docs/development/cli.mdx index 49aa9096..db28dfd5 100644 --- a/apps/docu/content/docs/development/cli.mdx +++ b/apps/docu/content/docs/development/cli.mdx @@ -3,7 +3,11 @@ title: "CLI Package" description: "TypeScript CLI to interact with the Basilic API via API key. Ideal for agentic integrations." --- -The `@repo/cli` package provides a command-line interface to the Basilic Fastify API. It uses `@repo/core` for all requests and supports only API key auth. Auth endpoints (magic link, OAuth, passkey, etc.) are excluded. +The `@repo/cli` package is the **API CLI** (`basilic` binary): call the Fastify API with an API key. It is not the project generator. + +The **project generator** is `create-basilic` (`npx create-basilic@latest my-app`). See [Getting Started](/docs/development) and [`tools/create-basilic/README.md`](https://github.com/blockmatic/basilic/blob/main/tools/create-basilic/README.md). + +The `@repo/cli` package uses `@repo/core` for all requests and supports only API key auth. Auth endpoints (magic link, OAuth, passkey, etc.) are excluded. ## Auth diff --git a/apps/docu/content/docs/development/cursor-skills.mdx b/apps/docu/content/docs/development/cursor-skills.mdx index 825c1108..17f567c9 100644 --- a/apps/docu/content/docs/development/cursor-skills.mdx +++ b/apps/docu/content/docs/development/cursor-skills.mdx @@ -33,7 +33,7 @@ Basilic refresh (Cursor, all skills, copies, non-interactive): pnpm dlx skills@latest add blockmatic/basilic-skills --skill '*' -a cursor --copy -y ``` -That writes tech skills under `.agents/skills//` and playbooks under `.agents/skills/workflow//`. Full installer options: catalog [README](https://github.com/blockmatic/basilic-skills#agents-and-paths). +That writes tech skills under `.agents/skills//` and playbooks under `.agents/skills/b/b-/`. Full installer options: catalog [README](https://github.com/blockmatic/basilic-skills#agents-and-paths). Root `skills-lock.json` tracks catalog hashes. FIRST is `source: blockmatic/first`. Commit the lock with `.agents/skills/` after every refresh. @@ -43,12 +43,28 @@ Root `skills-lock.json` tracks catalog hashes. FIRST is `source: blockmatic/firs **Tech** — `.agents/skills/-v/` (example: `fastify-v5`). Agent-invocable. Required sections: Scope, Assumptions, Principles, Constraints, Interactions. Force with `@.agents/skills/-v` or `/-v`. -**Workflow** — `.agents/skills/workflow//` (example: `workflow/plan-feature`). Slash-only. Set `disable-model-invocation: true`. One-line `description`. Not versioned. Type `/name` in Cursor. Claude Code has no `/` menu: read the `SKILL.md` file. +**Workflow** — install the parent `b` skill; nested children are not independent install targets. `.agents/skills/b/b-/` (example: `b/b-plan-feature`). Slash-only. Set `disable-model-invocation: true`. One-line `description`. Not versioned. Type `/b-` in Cursor. If a harness has no matching menu, read the `SKILL.md` file directly. `name` is lowercase letters, digits, and hyphens, max 64 characters, equal to the folder name. Skill descriptions are always in the agent’s discovery context. One folder per skill. Do not keep `foo/` next to `foo-v1/`. Prefer fewer, focused skills. +## Workflow migration + +The local workflow package is now `b`: `.agents/skills/b/SKILL.md` dispatches to 50 `/b-*` children, including `/b-build`. Shared authoring and completion references travel inside this tree. FIRST and technology skill names are unchanged. + +The catalog changes are prepared in the local `basilic-skills` checkout and are not yet published. Until they reach the canonical source, preview from that checkout: + +```bash +pnpm dlx skills@latest add /path/to/basilic-skills --skill b -a cursor --copy -y +``` + +After publication, substitute `blockmatic/basilic-skills` for the local path. Inspect the generated lockfile and installed files; do not assign an unpublished local preview a made-up GitHub hash. Avoid refreshing from the old remote revision during this preview because it still provides `workflow`. + +After verifying the new install, remove the old `workflow` package with the skills CLI, preserving any local edits first. This repository should have one workflow tree. Update `/workflow` to `/b`, `/plan-feature` to `/b-plan-feature`, and other standalone playbook names to their `b-` forms in saved prompts. `/b plan` is a shortcut to the same child; old names are not separate installed aliases. + +Packaging validation runs in the catalog with `pnpm validate`. It checks metadata, counts, grouping, and workflow reference targets. These checks do not prove that an agent follows the workflow or that a particular desktop menu has refreshed. + ## Versioning `-v` is the **library or SDK major**, not a skill-file revision. diff --git a/apps/docu/content/docs/development/dev-environments.mdx b/apps/docu/content/docs/development/dev-environments.mdx index a3bf6971..6d69e286 100644 --- a/apps/docu/content/docs/development/dev-environments.mdx +++ b/apps/docu/content/docs/development/dev-environments.mdx @@ -192,4 +192,4 @@ No special env vars are required to distinguish local vs remote. The same `.env` ## Related - [Dev Tooling](/docs/development/dev-tooling) – pnpm, Biome, ESLint, Turborepo -- [Onboard new developer](https://github.com/blockmatic/basilic/blob/main/.agents/skills/workflow/onboard-new-developer/SKILL.md) – Full onboarding process +- Onboard new developer — playbook `/b-onboard-new-developer` when installed under `.agents/skills/b/` (see [Cursor Skills workflow migration](/docs/development/cursor-skills#workflow-migration)) diff --git a/apps/docu/content/docs/development/index.mdx b/apps/docu/content/docs/development/index.mdx index 5df7c193..b11e9ca0 100644 --- a/apps/docu/content/docs/development/index.mdx +++ b/apps/docu/content/docs/development/index.mdx @@ -1,6 +1,6 @@ --- title: "Getting Started" -description: "Node 24, pnpm 11.24.0, clone, setup, and run the apps." +description: "Node 24, pnpm 11.24.0, create-basilic, setup, and run the apps." --- ## Prerequisites @@ -18,12 +18,12 @@ corepack prepare pnpm@11.24.0 --activate ## Setup ```bash -git clone -cd +npx create-basilic@latest my-app +cd my-app pnpm run setup ``` -Or GitHub **Use this template**, then clone your new repo. [After fork](/docs/development/after-fork) lists what to replace when the copy is your product. +Or clone this repository / GitHub **Use this template**, then `cd` into it. [After fork](/docs/development/after-fork) lists what to replace when the copy is your product. Forks are for contributing upstream. `setup` installs dependencies, git hooks, security tools, and database **tooling**. It also copies `.env..example` templates to gitignored dest files when those dest files are missing. Copied local env sets `ALLOW_TEST=true` so `test@test.ai` works without Resend. Edit the copied files to set paid secrets. `setup` does **not** start Postgres. @@ -35,7 +35,7 @@ pnpm dev - API: http://localhost:3001 (`GET /health`, `/reference`) - Web: http://localhost:3000 — first login is `/auth/login` with `test@test.ai` (copied env has `ALLOW_TEST=true`; Resend optional) -- Docs: `pnpm --filter @repo/docu dev` (http://localhost:3002) +- Docs: hosted [basilic-docs](https://basilic-docs.vercel.app/docs); in this repo `pnpm --filter @repo/docu dev` (http://localhost:3002). Generated projects use `docs/basilic/` instead. - Mobile: `pnpm --filter @repo/mobile start` **Apps:** `apps/api` (Fastify 5), `apps/web` (Next.js 16), `apps/mobile` (Expo 57), `apps/docu` (Fumadocs). diff --git a/apps/docu/content/docs/testing/product-ready.mdx b/apps/docu/content/docs/testing/product-ready.mdx index 93c36623..917f1597 100644 --- a/apps/docu/content/docs/testing/product-ready.mdx +++ b/apps/docu/content/docs/testing/product-ready.mdx @@ -3,16 +3,16 @@ title: Product Ready description: Fork-and-run bar for Basilic. CI green is Pipelines, not this bar. --- -**Product Ready** is a local clone or template that a new developer can start and sign into without tribal knowledge. It is **not** “CI is green.” Pipelines prove the repo on GitHub Actions. This page is the adopter bar. +**Product Ready** is a local generate/clone/template that a new developer can start and sign into without tribal knowledge. It is **not** “CI is green.” Pipelines prove the repo on GitHub Actions. This page is the adopter bar. -Canonical commands: [Getting Started](/docs/development). After you own the copy: [After fork](/docs/development/after-fork). Playbook: `/onboard-new-developer`. +Canonical commands: [Getting Started](/docs/development). After you own the copy: [After fork](/docs/development/after-fork). Playbook: `/b-onboard-new-developer`. ## Checklist Do these in order. `pnpm setup` installs tools and copies env templates. It does **not** start Postgres. 1. **Node 24 + pnpm** — `.nvmrc` / `packageManager` (`11.24.0`). `corepack enable` then `corepack prepare pnpm@11.24.0 --activate`. -2. **Clone** the repo, or GitHub **Use this template**. +2. **Create the repo** — `npx create-basilic@latest my-app`, or clone this repository, or GitHub **Use this template**. 3. **`pnpm setup`** at the repo root (install, hooks, scanners, env templates, Docker/Supabase CLI, Playwright Chromium). Copied API/web env sets `ALLOW_TEST=true` for local login. Setup copies templates only when dest files are **missing**; an existing `apps/api/.env` / `apps/web/.env.local` is left alone — set `ALLOW_TEST=true` yourself if those files predate this default. 4. **`pnpm --filter @repo/api db:start`** — local Supabase Postgres. One instance per host; `pnpm db:stop` before changing `project_id`. 5. **`pnpm reset`** at the repo root — Supabase reset + Drizzle migrate + seed. See [ADR 008](/docs/adrs/008-database) and `apps/api/README.md`. diff --git a/biome.json b/biome.json index 456b97b3..3ec70abb 100644 --- a/biome.json +++ b/biome.json @@ -35,7 +35,8 @@ "!**/next-env.d.ts", "!**/openapi/*.json", "!**/src/db/migrations/meta", - "!**/.deepsec" + "!**/.deepsec", + "!**/tools/create-basilic/template" ], "maxSize": 1048576 }, @@ -178,6 +179,14 @@ } } }, + { + "includes": ["**/tools/create-basilic/src/**"], + "linter": { + "rules": { + "suspicious": { "noConsole": "off" } + } + } + }, { "includes": [ "**/mdx-components.tsx", diff --git a/eslint.config.mjs b/eslint.config.mjs index b5fd9e75..cc4ef675 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,6 +12,6 @@ import { config } from '@repo/eslint-config/library.js' export default [ ...config, { - ignores: ['apps/**', 'packages/**', 'node_modules/**', '__dev/**', '.deepsec/**'], + ignores: ['apps/**', 'packages/**', 'tools/**', 'node_modules/**', '__dev/**', '.deepsec/**'], }, ] diff --git a/package.json b/package.json index f815e337..77a43977 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "basilic", + "version": "0.0.0", "private": true, "license": "MIT", "scripts": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 140805f4..fc888bf0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -98,7 +98,7 @@ importers: version: '@typescript/typescript6@6.0.2' vercel: specifier: latest - version: 59.11.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(bufferutil@4.1.0)(rollup@4.59.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) + version: 59.11.7(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(bufferutil@4.1.0)(rollup@4.59.0)(supports-color@10.2.2)(utf-8-validate@6.0.6) vitest: specifier: ^4.1.11 version: 4.1.11(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.4.0))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.25.10)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.12)(yaml@2.9.0)) @@ -1091,6 +1091,40 @@ importers: specifier: ^5.0.0 version: 5.0.0(zod@4.5.2) + tools/create-basilic: + dependencies: + commander: + specifier: ^15.0.0 + version: 15.0.0 + devDependencies: + '@repo/eslint-config': + specifier: workspace:* + version: link:../eslint + '@repo/typescript-config': + specifier: workspace:* + version: link:../typescript + '@types/node': + specifier: 24.13.3 + version: 24.13.3 + '@typescript/native': + specifier: npm:typescript@^7.0.2 + version: typescript@7.0.2 + eslint: + specifier: ^10.9.1 + version: 10.9.1(jiti@2.7.0)(supports-color@10.2.2) + tsup: + specifier: ^8.5.1 + version: 8.5.1(@typescript/typescript6@6.0.2)(jiti@2.7.0)(postcss@8.5.23)(supports-color@10.2.2)(tsx@4.23.12)(yaml@2.9.0) + tsx: + specifier: ^4.23.12 + version: 4.23.12 + typescript: + specifier: npm:@typescript/typescript6@^6.0.2 + version: '@typescript/typescript6@6.0.2' + vitest: + specifier: ^4.1.11 + version: 4.1.11(@edge-runtime/vm@3.2.0)(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/coverage-v8@4.1.11)(jsdom@30.0.1(@noble/hashes@2.4.0))(vite@8.2.2(@types/node@24.13.3)(esbuild@0.25.10)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.12)(yaml@2.9.0)) + tools/eslint: devDependencies: '@eslint/js': @@ -6028,20 +6062,20 @@ packages: '@upsetjs/venn.js@2.0.0': resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==} - '@vercel/backends@7.0.1': - resolution: {integrity: sha512-y5tIFDc8MBb4s82hYeTpXKM4GhWWumU6sT0pLLOAx2YEt6Z0Fr4V4Xy3fKysm5OMpO5HJVJ6k5uW2dhgW7JyhA==} + '@vercel/backends@7.0.2': + resolution: {integrity: sha512-tC1EQ7g7wJTUH4MYc1ve9dDn6l92aLR5VOSChan9slLctLmhOPFfZGD8R0ZlYzkTZLE50NVi6bMwE3jlvdza4Q==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/blob@2.2.0': resolution: {integrity: sha512-h9ruqqTyAlLrmLIT55NUDnfc889feZnQXWOBxpXrAiAzWKmHqIokLchuMBKFsT9MDV/jM0HRQlvlTyubMHrOKA==} engines: {node: '>=20.0.0'} - '@vercel/build-utils@14.9.0': - resolution: {integrity: sha512-czxOQSyZgFYZoD72gRSkRSokGghDyh5pMBPiuy0bjq2I4t7vjiENF1FN0NI/em5S/ITh2hKN1kzXHFwQy9jg6g==} + '@vercel/build-utils@14.9.1': + resolution: {integrity: sha512-d7Ov4+TA+UAxopVsgh698LPs0Um/LrW430FRU/qPlI7WFWWU55XwGJ2+aXCY8ZbA2zoZ8XjbZJVGjwnTCBG52Q==} - '@vercel/cervel@0.1.60': - resolution: {integrity: sha512-UPe3kleYEqs1DeyOr3BmY/UFIU5omcLEogSWQy42l003T9JRgKscvkQc25cy1fMpicciX9aXc7wmyZz6k6gJhQ==} + '@vercel/cervel@0.1.61': + resolution: {integrity: sha512-cm3WyokqnHwtLIK+k9C+NbUSmu+yGldnCPnn/rYB0ZzPZTlwdI3k7/d93Zq7/TEcsbHFpo7fxAwWr+DUAh3ufw==} hasBin: true '@vercel/cli-auth@0.3.5': @@ -6050,32 +6084,32 @@ packages: '@vercel/cli-config@0.2.4': resolution: {integrity: sha512-kZ5SojbrV06GHoU6QIWGwDXLov+s9rWZ7QqdqKfJfBGCNUieGfgaCjeeenNy8Y+QC0bwC0dZ2B4l5Hvdmrgpdw==} - '@vercel/container@7.0.0': - resolution: {integrity: sha512-VhQAJv8j6/ufedWYAbwjJ94p3R0MfNYiJmamr68NeFVGJlv6sFNm1SJb1Rzl+6udK44BcQp3M64qKFO4ARNLFA==} + '@vercel/container@7.0.1': + resolution: {integrity: sha512-kAN2pV5u3kj6AoyqeFa/PrlO9yxceTRBJM5YNJObdqpVVyLC7wlZ3GUx0VpP3TPsSFKK+B12xJPAP0xxxF6CWQ==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/detect-agent@1.2.5': resolution: {integrity: sha512-0krENrjuitlW8s6TJu0MlqCevyCU7K7JK63jZAf7xZ6n17tx+vUEwzHT3sTxawtwZxaW21hu+oFUpoOrm49FsQ==} engines: {node: '>=14'} - '@vercel/elysia@7.0.0': - resolution: {integrity: sha512-EZgu9HXQVf1af7sElg3tws+0TTT/k1DgPyDftJnXfk1L3W8M31pqbBY3a6I1hcnWxNaNceF2VtSWMPNrmsTOVQ==} + '@vercel/elysia@7.0.1': + resolution: {integrity: sha512-bOCgbU/d0qwOi+3vEOtkQ9oGzrs7CQ6PNcUM8L8iZ/cUirD6lra+T+wQgNEeOPi1q6xNcXCkj92J4uk/jEmMuw==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/error-utils@2.2.1': resolution: {integrity: sha512-9DhP8jP7raLML4hGsBemxX5fXuQnu5xxMV+HjGygGbzEmVK/+KyJ3QP2Cw7PdF0uXdb9N0Qa4c3tRGH34ZX6vw==} - '@vercel/express@7.0.1': - resolution: {integrity: sha512-aqGAtn9U3Myk5HOQRNH8DpYHcgjBo59+rCW2EDzuMCGIBYOeNPx7zAoOxUcwt/YFqldXrzM+R8FjFe3eLreHBQ==} + '@vercel/express@7.0.2': + resolution: {integrity: sha512-pRsUuf0YNg0gTypzV1Ne0Z1oO76KnLNVht8HP4ERHit3QS5RrCSh468CgqAB1tbVFexcSgFFTtJ/90oPwDEO9g==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/fastify@7.0.0': - resolution: {integrity: sha512-tcB2pd0DdQls884nS70bkZgdrYRFceST1zZO/073o1iJG0VVoFcIPuDdePGlIIetegGJ+S68Wc47gr4TU/UQcQ==} + '@vercel/fastify@7.0.1': + resolution: {integrity: sha512-oXnXkK7ydsQQO+Ds2Ivfsyqc1MeqJZxbiUvDX+oAG+jZjtTDjkJfs0sD8acKdVUtShFO55m9k8WzTILVaQVVRg==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/fun@1.3.0': resolution: {integrity: sha512-8erw9uPe0dFg45THkNxmjtvMX143SkZebmjgSVbcM3XCkXu3RIiBaJMcMNG8aaS+rnTuw8+d4De9HVT0M/r3wg==} @@ -6084,53 +6118,53 @@ packages: '@vercel/gatsby-plugin-vercel-analytics@1.0.12': resolution: {integrity: sha512-Ejlhwxr7EBYJxtwYnlh6Vm6A2dPsUSPxMdYrfv0koZkgAzVwo7cG4aDQiy7iASMaGzwuI/PAnwlY2sJBF5b4OA==} - '@vercel/gatsby-plugin-vercel-builder@2.2.52': - resolution: {integrity: sha512-PYl9TBsYsP0a7qB4kgrF0a5YBUt7caiZwrxd4dl/uPdWHZlogTrnw2Cf2GS/kgij4rsew5jhc9xMnE4eHvQ1zg==} + '@vercel/gatsby-plugin-vercel-builder@2.2.53': + resolution: {integrity: sha512-2AaKdNQGz5nQDQ9hMG8SeE7NdysDG7DYNK90YlNvRTqVopCvC6ZLAz33QuFc8dnAMAHiqYDOO0UKw+Px1KlBkg==} - '@vercel/go@10.0.0': - resolution: {integrity: sha512-G2tHOrb64snuckAdfq+OjMqE8fKIB4rq3FpbmaQ1vjvnyK/PqyWnvX9gCoIigThM49P0RkGQVp76DCt1RFGh4Q==} + '@vercel/go@10.0.1': + resolution: {integrity: sha512-qtxY5K4I5c/ll9JgGvxJ2as9mQDKzoiCsvxVB1F4VzsGh03EXJVYdYZoDdBcumdC+q3sy9G4a3UYhIpOspY+8Q==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/h3@7.0.0': - resolution: {integrity: sha512-GYhMAK/XgDAQmgXSaiokz2kjc8QeE4azNg9aajcAm2q16Yd+t4a+VZutHppfk4I5SaLf5sKAGhGB8hkCHkbTPA==} + '@vercel/h3@7.0.1': + resolution: {integrity: sha512-91utvhU1sigL/MfWgshQ8okSfR4Dfx8iA4kMuA1l2iCRW9cfS3YZxZh6F3IMPyiC/J75e9pCevpf9ze4OHWaNg==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/hono@7.0.0': - resolution: {integrity: sha512-Bn+illFuXukoI0l2z9Y75IS0c8mnLwy0zf5D0AIC2vegjPy5PnoYZ8c7mnEh88On/2ahMR3KEfocWnDssbpGHQ==} + '@vercel/hono@7.0.1': + resolution: {integrity: sha512-ZMDgAZ3RML7M05dUfogmLLLfE2JCJkJTfaCnF6oq9cH/BA0DTxn6QFQvQbIgiA18Ot0FcypuYRl9uge8hdCiYA==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/hydrogen@8.0.0': - resolution: {integrity: sha512-Z2CKkTKn2SsqD5ftXXizPJ2HCmUoWvvEL9RnSd/eup1IPpOMK42MjIGyh+aPiiwzOwcPmoANPCmMufqrUOjHzw==} + '@vercel/hydrogen@8.0.1': + resolution: {integrity: sha512-2CfTbi2XujdyNK/YQxs23j0Llm+jwyZYkysu+NSD8FO/vrpFzK9ozAmlWN+88S4phXoJjnfq35nDGf23YKRkDg==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/koa@7.0.0': - resolution: {integrity: sha512-UgfQVMc2+hjfoMrGmAFhHmGIf1uDwNL+3Y2B+Ad9Gn4D2wz2gn2Pl5ywy23VOmfSD/aTerbLigTWKT+y9fb9Tg==} + '@vercel/koa@7.0.1': + resolution: {integrity: sha512-FnQHRkRaL3w1NI+xBznOgFwuu0YfDvqdIdB384KdXTRYjZs3yhBfIC9GY0qmTu69Bh3Sln7ZANANZAK+7hbwmg==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/nestjs@7.0.0': - resolution: {integrity: sha512-suD7cuigAQlLA/RLAly8234gXXgIC/XG7AGBa1h2Y4vuIwGP43EBHZ4IuojdN6YCKwbD4/2eUMRa/qBs9OpsNQ==} + '@vercel/nestjs@7.0.1': + resolution: {integrity: sha512-kRmI/lJ3w4SpRjtCKZMxt69JSZDQn6ulRGAmrkr/p6ioYVstC1Uf94zRPjafkBIVYJFW0KExNq/kwv5XYU59bw==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/next@11.0.1': - resolution: {integrity: sha512-DoEGoXoAJZW8cl0HZ7V2q0aBb6vZTriPA0jeFGM4gvPC6fFA3PQzGfJMY6Fw7VcPeOAtn/QEB8ey4Az/dkiTAQ==} + '@vercel/next@11.0.2': + resolution: {integrity: sha512-c97ybY1jpF7534R0DkxlydWOOcFg93X24rdEdljBHtnho3wWD2RE+E9beC5dzq98uXSYSXrUQUghQko8oiY8sg==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/nft@1.10.0': resolution: {integrity: sha512-iLOW4fcsgkipfOh2Bw3wB38YDfxTlxr7+j4uFeui2OswkNT28jIitS/aMce7tS0mef1YPQ8zLIDYr3a0aahNrA==} engines: {node: '>=20'} hasBin: true - '@vercel/node@12.0.0': - resolution: {integrity: sha512-F3tbqSdN1Nap1zeZcdfScatAVFUrLLYXNBsHf9wutOMyOjuW7M32NEEp1eXhjHXdrIg9SyChooqvJPmt3iepSw==} + '@vercel/node@12.0.1': + resolution: {integrity: sha512-aKxdgnp31N/clDxZM69Z9McoZ0gfBo48Ms+iiuFxJ5wt2SilTVvmS3Rn/LZjlTvQwHGKlJPUwamiuDs/Qsfszw==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/oidc@3.2.0': resolution: {integrity: sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug==} @@ -6142,59 +6176,59 @@ packages: '@vercel/python-analysis@0.14.0': resolution: {integrity: sha512-qyVxbaU14gAi/AsaR8syZLukUsOp69Jkm1xn80rZPy4k9+zRUTIfqs0AOk7L8wwBxDDB6LLjcBUAmafhbJQnUQ==} - '@vercel/python@13.0.0': - resolution: {integrity: sha512-KUqi9G5jx26m10kbpzgd8q2jGlijgX4aawH5aD2J8T7pu4q0dZGTw0DlczdrSFnEDYd8yM3zghGjXjtvcrKeaQ==} + '@vercel/python@13.0.1': + resolution: {integrity: sha512-w96ozfdjVQxkPXKcY6GEi7/gqc9ZLriiWiflrUS/fBq66gTBkWtga2CFhK166ZaQT+cqgN3/X0NED7gUUzoLGw==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/redwood@9.0.0': - resolution: {integrity: sha512-y4YdEsqjGVHFcLTPKZWppTvcgXbq9edxxXl+KBEJvtJpqY7s4ZjFd+BzF8YF6KTC7x18rYOiugBSrbyLyctRtQ==} + '@vercel/redwood@9.0.1': + resolution: {integrity: sha512-gamcb5L0T5HA11+10kBxypGbxSm3wTzWcor6qCpJdO3YMNzbvf74PZmwPfEMEsMo9BBb0MV03XgsVObra5lriw==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/remix-builder@12.0.0': - resolution: {integrity: sha512-SQqmOQSQn44Migiu/xglGZ2EjlE4YfEEi8GpOHfN2iVKZhGqyeA5spTzVuxWAHjxeYbaOarHW7qM/giaFc0e2A==} + '@vercel/remix-builder@12.0.1': + resolution: {integrity: sha512-7X5F2f61bUANZ1NawaXRZh322sHR4RLMADrOKIbteCeMUw1me2FhgM5EjYWxGwCwkD2tmveo+CtDU5cESsmLag==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/ruby@9.0.0': - resolution: {integrity: sha512-kTmJZWkZpSEcK1t0FuM1Py1PTLDTgGbmhwXt1B1Tv/ZpJU2UfLK9rxzNUZemy2jdxSRtFWL+D/Ur0j1sF1W7Hg==} + '@vercel/ruby@9.0.1': + resolution: {integrity: sha512-DwqWxQiA7DSnA1/Xr5EkZ8tCfeZ/yc/zbX15DfwBbHsXdcX4dnKLjjQNZan/6zCehHRWstEQja9JMohRK39oeQ==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/rust@8.0.0': - resolution: {integrity: sha512-Ut16g8BZkwedJ8NN+LlPZPbiy4sC4efYYl2f440A6dg7obW5t4xjt9M1wTkooIgT0w2/j/FVfDdmQNJGfpPCgA==} + '@vercel/rust@8.0.1': + resolution: {integrity: sha512-rCXhlyA4P80cXpIsMAueVeymsia8dlyZwFJNXyYKOxeMnVasZsDJ06/xuObwLndww16IaNrGtkf9GQ7uU1JVwA==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/sandbox@3.1.0': resolution: {integrity: sha512-z124E4rsmNpwGtTnLImiYzEXk972bWniQyhlvkEt35UtwKTdfBAFXcNG2OvqYqwzAsdQaP3B7teQ2pqA9B5Viw==} - '@vercel/static-build@9.0.0': - resolution: {integrity: sha512-JrYaWxWmq83vQofB669VTp9egqoJN4wn3JgduvgCj3mNsdRj7R2wvyMPdGJWNm6t5Jsq3YMikik9VVmxZpctRQ==} + '@vercel/static-build@9.0.1': + resolution: {integrity: sha512-n/4AxdEBprOo/1WEHyQfZdYKw1fmAn54SQGWww0ZP3WRkbYRKui1KjkfqeHLdSkhu6AAsZGpRlYZmK5sghFErA==} peerDependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/static-config@3.4.3': resolution: {integrity: sha512-BY1sL8rNJvIm3TK/8TQ+Q6jIx7NpO5xckQzv7s6c1ypHvRnTl+vf6rmgY5WFXmoYDeGm3tMy4jiy/5M/5jGr/g==} - '@vercel/vc-native-darwin-arm64@59.11.2': - resolution: {integrity: sha512-Uay/TyJ8e6u1PHedoNsYYkZoi0RCGhOdBZwk9JWowk/MZF1wKEZ1znveXf0F14iPt+IOuEr/gkaNKow5dGGz9Q==} + '@vercel/vc-native-darwin-arm64@59.11.7': + resolution: {integrity: sha512-SeYVrBni2n2GiUdyfLQb8R5ZSzujljsAePMPOlXg3nIC+oxyFjfmZn/vZmDfjfa5bV/Qme/LRtsU5GJQiSE08w==} cpu: [arm64] os: [darwin] - '@vercel/vc-native-darwin-x64@59.11.2': - resolution: {integrity: sha512-3D6q80gH6aJiOCE0JFyGfYR9yybMCammSxiOjLzosGgz3fiW+SbLrBE8IBW52aFLvvrJF5nGcXRKORL6o7hKAw==} + '@vercel/vc-native-darwin-x64@59.11.7': + resolution: {integrity: sha512-SAwmxo3kfCXWO32Iq9A1VMVRVWi0B0G1zCPO16Fiuz0eFK/PZRnWXbA2J68U6W3hkf4nCpC6IEagNBhJudA72w==} cpu: [x64] os: [darwin] - '@vercel/vc-native-linux-arm64@59.11.2': - resolution: {integrity: sha512-N+UXVw9rKXTH4eylgGVTDuM2ezo1H+NKRI730EkOMxv5snpaER+qzmjVgzQIrB4n0GbN8Se72ow3iNAL9Una3g==} + '@vercel/vc-native-linux-arm64@59.11.7': + resolution: {integrity: sha512-UbyQVlA59SPsQmSfUUAdMm37bOVJti/GbcsEF4QwFIsWjjV2wPFJkfzSICeE25xw/TSWCNmVD4GqjXJEnzAGcQ==} cpu: [arm64] os: [linux] - '@vercel/vc-native-linux-x64@59.11.2': - resolution: {integrity: sha512-tZ1QHiQcdHcQKeHBKLdGecxc6ylc+OADUaL2/1iI7CVsZ7bwwzsH3HrgmELNOJfR/seNC+D21sytjhl4xRWGEA==} + '@vercel/vc-native-linux-x64@59.11.7': + resolution: {integrity: sha512-zabEvXd4VDxV8kYl1vXHBqJGiG354EWrEAoJD+zTuwQxu4bM38nZON96OvsQEGJt4Q1WG0cjFwPFfqEugrUOfQ==} cpu: [x64] os: [linux] @@ -12180,8 +12214,8 @@ packages: vconsole@3.15.1: resolution: {integrity: sha512-KH8XLdrq9T5YHJO/ixrjivHfmF2PC2CdVoK6RWZB4yftMykYIaXY1mxZYAic70vADM54kpMQF+dYmvl5NRNy1g==} - vercel@59.11.2: - resolution: {integrity: sha512-1MY49OhofgVO98dICAHz1jSiU2dxVXwstTpIYmR4czsg/NjUeCs78l/kdLhw/ITi3ET7Rxq1/5eqYsRmmPAhzQ==} + vercel@59.11.7: + resolution: {integrity: sha512-C+L/JKmlGDypKGcTU/atckydeK/AKa/7fKwUbvcwveguV1QPlY8beiIGgbwkdbb80bbIpPFHRQYrhi5XPAmCBA==} engines: {node: '>= 18'} hasBin: true @@ -17765,9 +17799,9 @@ snapshots: d3-selection: 3.0.0 d3-transition: 3.0.1(d3-selection@3.0.0) - '@vercel/backends@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/backends@7.0.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) execa: 3.2.0 @@ -17796,14 +17830,14 @@ snapshots: throttleit: 2.1.0 undici: 7.29.0 - '@vercel/build-utils@14.9.0': + '@vercel/build-utils@14.9.1': dependencies: cjs-module-lexer: 1.2.3 es-module-lexer: 1.5.0 - '@vercel/cervel@0.1.60(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/cervel@0.1.61(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/backends': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/backends': 7.0.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' @@ -17825,16 +17859,16 @@ snapshots: xdg-app-paths: 5.5.1 zod: 4.1.11 - '@vercel/container@7.0.0(@vercel/build-utils@14.9.0)': + '@vercel/container@7.0.1(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/detect-agent@1.2.5': {} - '@vercel/elysia@7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/elysia@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/build-utils': 14.9.1 + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' @@ -17845,12 +17879,12 @@ snapshots: '@vercel/error-utils@2.2.1': {} - '@vercel/express@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/express@7.0.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 - '@vercel/cervel': 0.1.60(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/build-utils': 14.9.1 + '@vercel/cervel': 0.1.61(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) fs-extra: 11.1.0 path-to-regexp: 6.3.0 @@ -17863,10 +17897,10 @@ snapshots: - rollup - supports-color - '@vercel/fastify@7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/fastify@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/build-utils': 14.9.1 + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' @@ -17903,22 +17937,22 @@ snapshots: dependencies: web-vitals: 0.2.4 - '@vercel/gatsby-plugin-vercel-builder@2.2.52': + '@vercel/gatsby-plugin-vercel-builder@2.2.53': dependencies: '@sinclair/typebox': 0.25.24 - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 esbuild: 0.25.10 etag: 1.8.1 fs-extra: 11.1.0 - '@vercel/go@10.0.0(@vercel/build-utils@14.9.0)': + '@vercel/go@10.0.1(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/h3@7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/h3@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/build-utils': 14.9.1 + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' @@ -17927,11 +17961,11 @@ snapshots: - rollup - supports-color - '@vercel/hono@7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/hono@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) fs-extra: 11.1.0 path-to-regexp: 6.3.0 @@ -17944,19 +17978,19 @@ snapshots: - rollup - supports-color - '@vercel/hydrogen@8.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)': + '@vercel/hydrogen@8.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) ts-morph: 12.0.0 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' - '@vercel/koa@7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/koa@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/build-utils': 14.9.1 + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' @@ -17965,10 +17999,10 @@ snapshots: - rollup - supports-color - '@vercel/nestjs@7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/nestjs@7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/build-utils': 14.9.1 + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) transitivePeerDependencies: - '@emnapi/core' @@ -17977,9 +18011,9 @@ snapshots: - rollup - supports-color - '@vercel/next@11.0.1(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/next@11.0.2(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) transitivePeerDependencies: - encoding @@ -18005,13 +18039,13 @@ snapshots: - rollup - supports-color - '@vercel/node@12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/node@12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: '@edge-runtime/node-utils': 2.3.0 '@edge-runtime/primitives': 4.1.0 '@edge-runtime/vm': 3.2.0 '@types/node': 24.13.3 - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/error-utils': 2.2.1 '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) @@ -18050,14 +18084,14 @@ snapshots: smol-toml: 1.6.1 zod: 3.22.4 - '@vercel/python@13.0.0(@vercel/build-utils@14.9.0)': + '@vercel/python@13.0.1(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/python-analysis': 0.14.0 - '@vercel/redwood@9.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/redwood@9.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) semver: 6.3.1 @@ -18069,9 +18103,9 @@ snapshots: - rollup - supports-color - '@vercel/remix-builder@12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2)': + '@vercel/remix-builder@12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/error-utils': 2.2.1 '@vercel/nft': 1.10.0(rollup@4.59.0)(supports-color@10.2.2) '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) @@ -18085,13 +18119,13 @@ snapshots: - rollup - supports-color - '@vercel/ruby@9.0.0(@vercel/build-utils@14.9.0)': + '@vercel/ruby@9.0.1(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 - '@vercel/rust@8.0.0(@vercel/build-utils@14.9.0)': + '@vercel/rust@8.0.1(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 execa: 5.1.1 get-port: 5.1.1 smol-toml: 1.6.1 @@ -18114,11 +18148,11 @@ snapshots: - bare-abort-controller - react-native-b4a - '@vercel/static-build@9.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)': + '@vercel/static-build@9.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)': dependencies: - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/gatsby-plugin-vercel-analytics': 1.0.12 - '@vercel/gatsby-plugin-vercel-builder': 2.2.52 + '@vercel/gatsby-plugin-vercel-builder': 2.2.53 '@vercel/static-config': 3.4.3(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3) ts-morph: 12.0.0 transitivePeerDependencies: @@ -18135,16 +18169,16 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - '@vercel/vc-native-darwin-arm64@59.11.2': + '@vercel/vc-native-darwin-arm64@59.11.7': optional: true - '@vercel/vc-native-darwin-x64@59.11.2': + '@vercel/vc-native-darwin-x64@59.11.7': optional: true - '@vercel/vc-native-linux-arm64@59.11.2': + '@vercel/vc-native-linux-arm64@59.11.7': optional: true - '@vercel/vc-native-linux-x64@59.11.2': + '@vercel/vc-native-linux-x64@59.11.7': optional: true '@vitejs/plugin-react@6.1.1(babel-plugin-react-compiler@1.0.0)(vite@8.2.2(@types/node@24.13.3)(esbuild@0.25.10)(jiti@2.7.0)(terser@5.51.2)(tsx@4.23.12)(yaml@2.9.0))': @@ -25426,35 +25460,35 @@ snapshots: core-js: 3.50.0 mutation-observer: 1.0.3 - vercel@59.11.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(bufferutil@4.1.0)(rollup@4.59.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): + vercel@59.11.7(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(bufferutil@4.1.0)(rollup@4.59.0)(supports-color@10.2.2)(utf-8-validate@6.0.6): dependencies: - '@vercel/backends': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/backends': 7.0.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/blob': 2.2.0 - '@vercel/build-utils': 14.9.0 + '@vercel/build-utils': 14.9.1 '@vercel/cli-auth': 0.3.5 '@vercel/cli-config': 0.2.4 - '@vercel/container': 7.0.0(@vercel/build-utils@14.9.0) + '@vercel/container': 7.0.1(@vercel/build-utils@14.9.1) '@vercel/detect-agent': 1.2.5 - '@vercel/elysia': 7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/express': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/fastify': 7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/elysia': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/express': 7.0.2(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/fastify': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/fun': 1.3.0(supports-color@10.2.2) - '@vercel/go': 10.0.0(@vercel/build-utils@14.9.0) - '@vercel/h3': 7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/hono': 7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/hydrogen': 8.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0) - '@vercel/koa': 7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/nestjs': 7.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/next': 11.0.1(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/node': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/go': 10.0.1(@vercel/build-utils@14.9.1) + '@vercel/h3': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/hono': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/hydrogen': 8.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1) + '@vercel/koa': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/nestjs': 7.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/next': 11.0.2(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/node': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) '@vercel/prepare-flags-definitions': 0.3.0 - '@vercel/python': 13.0.0(@vercel/build-utils@14.9.0) + '@vercel/python': 13.0.1(@vercel/build-utils@14.9.1) '@vercel/python-analysis': 0.14.0 - '@vercel/redwood': 9.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/remix-builder': 12.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0)(rollup@4.59.0)(supports-color@10.2.2) - '@vercel/ruby': 9.0.0(@vercel/build-utils@14.9.0) - '@vercel/rust': 8.0.0(@vercel/build-utils@14.9.0) - '@vercel/static-build': 9.0.0(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.0) + '@vercel/redwood': 9.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/remix-builder': 12.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1)(rollup@4.59.0)(supports-color@10.2.2) + '@vercel/ruby': 9.0.1(@vercel/build-utils@14.9.1) + '@vercel/rust': 8.0.1(@vercel/build-utils@14.9.1) + '@vercel/static-build': 9.0.1(@emnapi/core@1.10.0)(@emnapi/runtime@1.11.3)(@vercel/build-utils@14.9.1) chokidar: 4.0.0 esbuild: 0.25.10 jose: 5.9.6 @@ -25466,10 +25500,10 @@ snapshots: uuid: 14.0.1 zod: 4.1.11 optionalDependencies: - '@vercel/vc-native-darwin-arm64': 59.11.2 - '@vercel/vc-native-darwin-x64': 59.11.2 - '@vercel/vc-native-linux-arm64': 59.11.2 - '@vercel/vc-native-linux-x64': 59.11.2 + '@vercel/vc-native-darwin-arm64': 59.11.7 + '@vercel/vc-native-darwin-x64': 59.11.7 + '@vercel/vc-native-linux-arm64': 59.11.7 + '@vercel/vc-native-linux-x64': 59.11.7 transitivePeerDependencies: - '@emnapi/core' - '@emnapi/runtime' diff --git a/release-please-config.json b/release-please-config.json new file mode 100644 index 00000000..0e4f67fe --- /dev/null +++ b/release-please-config.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json", + "packages": { + ".": { + "release-type": "node", + "include-component-in-tag": false, + "include-v-in-tag": true, + "changelog-path": "CHANGELOG.md", + "extra-files": [ + { + "type": "json", + "path": "tools/create-basilic/package.json", + "jsonpath": "$.version" + } + ] + } + }, + "changelog-sections": [ + { "type": "feat", "section": "Features" }, + { "type": "fix", "section": "Bug Fixes" }, + { "type": "perf", "section": "Performance" }, + { "type": "revert", "section": "Reverts" }, + { "type": "docs", "section": "Documentation", "hidden": true }, + { "type": "chore", "section": "Miscellaneous", "hidden": true }, + { "type": "test", "section": "Tests", "hidden": true }, + { "type": "ci", "section": "Continuous Integration", "hidden": true }, + { "type": "style", "section": "Styles", "hidden": true }, + { "type": "refactor", "section": "Code Refactoring", "hidden": true } + ], + "bootstrap-sha": "f6b9880a0ea6434e1d91423e8eccc6bf2f0d4045" +} diff --git a/scripts/README.md b/scripts/README.md index 7c8b0ba8..20c3bb3b 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -49,6 +49,22 @@ Runs during `postpack` lifecycle hook (after packing): **Package Configuration**: Packages using these scripts should have development exports pointing to `src/` in `package.json`. See [Publishing Guide](@apps/docu/content/docs/deployment/publishing.mdx) for complete configuration details. +`create-basilic` does **not** use these scripts. Pack it with `npm pack` from `tools/create-basilic`. + +## Generator + +### `assert-generated-tree.mjs` + +Fails if an assembled template still contains forbidden paths (`apps/docu`, the generator, Release Please) or is missing required agent/docs files. + +```bash +node scripts/assert-generated-tree.mjs /path/to/assembled-template +``` + +### `release-impact.mjs` + +Fails when starter payload paths change behind a `docs`/`chore`/`test`/`ci`/`style` PR title unless the body has `skip-release: true`. Used by `.github/workflows/release-impact.yml`. + ## Security Scripts Scripts that prevent committing secrets, scan for vulnerabilities, and install security tools. diff --git a/scripts/assert-generated-tree.mjs b/scripts/assert-generated-tree.mjs new file mode 100644 index 00000000..6e7e07cd --- /dev/null +++ b/scripts/assert-generated-tree.mjs @@ -0,0 +1,101 @@ +#!/usr/bin/env node +import { existsSync, readFileSync } from 'node:fs' +import { join } from 'node:path' + +const dest = process.argv[2] +if (!dest) { + console.error('Usage: assert-generated-tree.mjs ') + process.exit(1) +} + +const forbidden = [ + 'apps/docu', + 'tools/create-basilic', + 'scripts/prepare-publish.mjs', + 'scripts/restore-publish.mjs', + '_first/basilic', + 'release-please-config.json', + '.release-please-manifest.json', + '.github/workflows/release-please.yml', + '.github/workflows/publish-create-basilic.yml', + '.github/workflows/pr-title.yml', + '.github/workflows/scaffold.yml', + '.github/workflows/release-impact.yml', + '.github/workflows/scaffold-acceptance.yml', +] + +const required = [ + 'AGENTS.md', + 'LICENSE', + 'package.json', + 'pnpm-lock.yaml', + 'apps/api/package.json', + 'apps/web/package.json', + 'apps/mobile/package.json', + 'packages/cli/package.json', + 'tools/eslint/package.json', + 'tools/typescript/package.json', + '_first/FIRST.md', + '_first/PRODUCT.md', + 'docs/basilic/development/index.md', + 'docs/basilic/testing/product-ready.md', + '.cursor/rules/base/general.mdc', + '.agents/skills/b/SKILL.md', +] + +let failed = false +for (const path of forbidden) { + if (existsSync(join(dest, path))) { + console.error(`Forbidden path present: ${path}`) + failed = true + } +} +for (const path of required) { + if (!existsSync(join(dest, path))) { + console.error(`Required path missing: ${path}`) + failed = true + } +} + +const turboRaw = readFileSync(join(dest, 'turbo.json'), 'utf8') +if (turboRaw.includes('@repo/docu#build')) { + console.error('turbo.json still declares @repo/docu#build') + failed = true +} + +const license = readFileSync(join(dest, 'LICENSE'), 'utf8') +if (!license.includes('MIT')) { + console.error('LICENSE is not MIT') + failed = true +} + +const pkg = JSON.parse(readFileSync(join(dest, 'package.json'), 'utf8')) +if (pkg.name === 'create-basilic') { + console.error('assembled root package.json must not be the generator') + failed = true +} + +const workspace = readFileSync(join(dest, 'pnpm-workspace.yaml'), 'utf8') +if ( + !workspace.includes('apps/*') || + !workspace.includes('packages/*') || + !workspace.includes('tools/*') +) { + console.error('pnpm-workspace.yaml is missing apps/*, packages/*, or tools/*') + failed = true +} + +const lockPath = join(dest, 'skills-lock.json') +if (existsSync(lockPath)) { + const lock = JSON.parse(readFileSync(lockPath, 'utf8')) + const local = Object.entries(lock.skills ?? {}).filter( + ([, skill]) => skill.sourceType === 'local', + ) + if (local.length > 0) { + const names = local.map(([name]) => name).join(', ') + console.error(`skills-lock.json still has local sources: ${names}`) + failed = true + } +} + +process.exit(failed ? 1 : 0) diff --git a/scripts/release-impact.mjs b/scripts/release-impact.mjs new file mode 100644 index 00000000..c79ec6d0 --- /dev/null +++ b/scripts/release-impact.mjs @@ -0,0 +1,74 @@ +#!/usr/bin/env node +/** + * Fail when starter payload paths change behind a non-releasing PR title. + * Skip with `skip-release: true` in the PR body after review. + */ +import { spawnSync } from 'node:child_process' +import { dirname } from 'node:path' +import { fileURLToPath } from 'node:url' + +const repoRoot = dirname(dirname(fileURLToPath(import.meta.url))) + +const payloadPrefixes = [ + 'apps/api/', + 'apps/web/', + 'apps/mobile/', + 'packages/', + 'tools/eslint/', + 'tools/typescript/', + 'tools/create-basilic/', + 'scripts/', + '.cursor/', + '.agents/', + '.github/actions/', + 'AGENTS.md', + 'package.json', + 'pnpm-lock.yaml', + 'pnpm-workspace.yaml', + 'turbo.json', + 'biome.json', + 'eslint.config.mjs', +] + +const nonReleasing = /^(docs|chore|test|ci|style)(\(.+\))?:/ + +const title = process.env.PR_TITLE ?? '' +const body = process.env.PR_BODY ?? '' +const base = process.env.PR_BASE_SHA ?? 'origin/main' +const head = process.env.PR_HEAD_SHA ?? 'HEAD' + +if (/skip-release:\s*true/i.test(body) || /Release-As:/i.test(body)) { + console.log('Release-impact: skip-release or Release-As present; allowing non-releasing title.') + process.exit(0) +} + +if (!nonReleasing.test(title.trim())) { + console.log('Release-impact: title is release-bearing or unclassified; ok.') + process.exit(0) +} + +const diff = spawnSync('git', ['diff', '--name-only', `${base}...${head}`], { + cwd: repoRoot, + encoding: 'utf8', +}) +if (diff.status !== 0) { + console.error(diff.stderr || 'git diff failed') + process.exit(1) +} + +const changed = diff.stdout.split('\n').filter(Boolean) +const payload = changed.filter(path => + payloadPrefixes.some(prefix => path === prefix.replace(/\/$/, '') || path.startsWith(prefix)), +) + +if (payload.length === 0) { + console.log('Release-impact: no payload paths; ok.') + process.exit(0) +} + +console.error( + `Release-impact: payload paths changed with non-releasing title "${title}".\n` + + 'Use feat/fix/perf (or BREAKING CHANGE) or add `skip-release: true` to the PR body after review.\n\n' + + payload.join('\n'), +) +process.exit(1) diff --git a/scripts/run-qa.mjs b/scripts/run-qa.mjs index dbc78c8d..c6340965 100644 --- a/scripts/run-qa.mjs +++ b/scripts/run-qa.mjs @@ -46,7 +46,12 @@ const phases = [ 'packages/cli/src/gen', ], }, - { name: 'build', cmd: 'pnpm', args: ['build'], env: qaBuildEnv }, + { + name: 'build', + cmd: 'pnpm', + args: ['build'], + env: { ...qaBuildEnv, NEXT_PUBLIC_API_URL: 'http://localhost:3001' }, + }, ...(skipTests ? [] : [ diff --git a/skills-lock.json b/skills-lock.json index ec3bc41a..756702a6 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -19,6 +19,12 @@ "skillPath": "skills/ai-sdk-ui-v7/SKILL.md", "computedHash": "2d909a358b697da27416f5db98605d9cbf366600437faaa0a3c0c9d1c108ebdd" }, + "b": { + "source": "blockmatic/basilic-skills", + "sourceType": "github", + "skillPath": "skills/b/SKILL.md", + "computedHash": "5f7f062df2681c100f2e05f8a546842603412cd4f56dff706fcda81419ff6f47" + }, "drizzle-orm-v0": { "source": "blockmatic/basilic-skills", "sourceType": "github", diff --git a/tools/create-basilic/.gitignore b/tools/create-basilic/.gitignore new file mode 100644 index 00000000..30d90393 --- /dev/null +++ b/tools/create-basilic/.gitignore @@ -0,0 +1,3 @@ +dist +template +*.tgz diff --git a/tools/create-basilic/.npmignore b/tools/create-basilic/.npmignore new file mode 100644 index 00000000..3f4b67ac --- /dev/null +++ b/tools/create-basilic/.npmignore @@ -0,0 +1,7 @@ +* +!dist/ +!dist/** +!template/ +!template/** +!package.json +!README.md diff --git a/tools/create-basilic/README.md b/tools/create-basilic/README.md new file mode 100644 index 00000000..cefda4b0 --- /dev/null +++ b/tools/create-basilic/README.md @@ -0,0 +1,49 @@ +# create-basilic + +Scaffold an independent Basilic API, web, and mobile monorepo. The documentation app and this generator are not copied into the result. + +```bash +npx create-basilic@latest my-app +npx create-basilic@latest my-app --yes +npx create-basilic@1.2.3 my-app +``` + +Requires **Node.js 24.x** and, after generation, **pnpm 11.24.0**. + +`--yes` accepts safe defaults and never overwrites. The destination must be empty. Paths with spaces are supported. + +The published tarball includes a sanitized, lockfile-valid template. The CLI does not need Git, GitHub, or Basilic workspace packages at runtime. + +## After generate + +```bash +cd my-app +pnpm setup +pnpm --filter @repo/api db:start +pnpm reset +pnpm dev +``` + +Local starter docs: `docs/basilic/`. Hosted: [Product Ready](https://basilic-docs.vercel.app/docs/testing/product-ready). + +Generated projects retain the `/b` workflow catalog and its packaged checklists; documentation pointers are rewritten to the local snapshot. + +The API CLI remains `packages/cli` (`basilic` binary). This package is only the project generator. + +## Maintainers + +From the Basilic repo: + +```bash +pnpm --filter create-basilic test +CREATE_BASILIC_ALLOW_DIRTY=1 pnpm --filter create-basilic assemble +CREATE_BASILIC_LOCKFILE=1 pnpm --filter create-basilic assemble +CREATE_BASILIC_TEMPLATE_DIR=/tmp/basilic-template pnpm --filter create-basilic assemble +pnpm --filter create-basilic build +``` + +Assemble writes `tools/create-basilic/template/` by default (gitignored). Do not run `pnpm install` there — the parent workspace would claim it. CI assembles into `$RUNNER_TEMP` via `CREATE_BASILIC_TEMPLATE_DIR`. + +Do not run `scripts/prepare-publish.mjs` for this package. Pack with `npm pack` from `tools/create-basilic` so the parent `.gitignore` `dist`/`bin` rules do not drop the payload (this package has `.npmignore`). + +Generator copy tests run on Ubuntu, macOS, and Windows. Full-stack Product Ready remains Unix-oriented. diff --git a/tools/create-basilic/eslint.config.js b/tools/create-basilic/eslint.config.js new file mode 100644 index 00000000..9a84200c --- /dev/null +++ b/tools/create-basilic/eslint.config.js @@ -0,0 +1,15 @@ +import { config } from '@repo/eslint-config/library' + +export default [ + ...config, + { + ignores: ['dist/**', 'template/**'], + }, + { + files: ['src/**/*.ts'], + rules: { + 'no-restricted-properties': 'off', + 'turbo/no-undeclared-env-vars': 'off', + }, + }, +] diff --git a/tools/create-basilic/manifest.json b/tools/create-basilic/manifest.json new file mode 100644 index 00000000..f359876e --- /dev/null +++ b/tools/create-basilic/manifest.json @@ -0,0 +1,77 @@ +{ + "exclude": [ + "apps/docu/", + "tools/create-basilic/", + "scripts/prepare-publish.mjs", + "scripts/restore-publish.mjs", + "scripts/release-impact.mjs", + "scripts/assert-generated-tree.mjs", + "_first/basilic/", + "_first/templates/", + ".github/workflows/pr-title.yml", + ".github/workflows/scaffold.yml", + ".github/workflows/release-please.yml", + ".github/workflows/publish-create-basilic.yml", + ".github/workflows/release-impact.yml", + ".github/workflows/scaffold-acceptance.yml", + ".github/PULL_REQUEST_TEMPLATE.md", + "release-please-config.json", + ".release-please-manifest.json", + "CHANGELOG.md", + "__dev/" + ], + "transform": [ + "package.json", + "README.md", + "AGENTS.md", + "CLAUDE.md", + "turbo.json", + "skills-lock.json", + ".coderabbit.yaml", + ".cursor/", + ".agents/", + "_first/", + ".deepsec/", + "scripts/run-qa.mjs", + "scripts/README.md", + "apps/api/", + "apps/web/", + "apps/mobile/", + "packages/cli/", + ".github/workflows/" + ], + "include": [ + "apps/api/", + "apps/web/", + "apps/mobile/", + "packages/", + "tools/eslint/", + "tools/typescript/", + "scripts/", + ".github/", + ".cursor/", + ".agents/", + ".deepsec/", + "_first/", + ".vscode/", + "AGENTS.md", + "CLAUDE.md", + "LICENSE", + "README.md", + "biome.json", + "eslint.config.mjs", + "osv-scanner.toml", + "package.json", + "pnpm-lock.yaml", + "pnpm-workspace.yaml", + "skills-lock.json", + "tsconfig.json", + "turbo.json", + ".gitignore", + ".gitleaks.toml", + ".node-version", + ".nvmrc", + ".trufflehogignore", + ".coderabbit.yaml" + ] +} diff --git a/tools/create-basilic/package.json b/tools/create-basilic/package.json new file mode 100644 index 00000000..2827f660 --- /dev/null +++ b/tools/create-basilic/package.json @@ -0,0 +1,55 @@ +{ + "name": "create-basilic", + "version": "0.0.0", + "type": "module", + "private": false, + "license": "MIT", + "description": "Scaffold an independent Basilic API, web, and mobile monorepo", + "bin": { + "create-basilic": "./dist/cli.js" + }, + "files": [ + "dist", + "template", + "README.md", + "package.json" + ], + "exports": { + ".": { + "types": "./src/cli.ts", + "import": "./dist/cli.js", + "default": "./dist/cli.js" + } + }, + "scripts": { + "assemble": "tsx src/assemble/run.ts", + "build": "tsup", + "checktypes": "tsc --noEmit", + "lint:eslint": "eslint . --max-warnings 0", + "lint:eslint:fix": "eslint . --fix --max-warnings 0", + "test": "pnpm test:unit", + "test:unit": "vitest run", + "prepack": "node -e \"require('node:fs').accessSync('template/package.json')\"" + }, + "dependencies": { + "commander": "^15.0.0" + }, + "devDependencies": { + "@repo/eslint-config": "workspace:*", + "@repo/typescript-config": "workspace:*", + "@types/node": "^24.13.3", + "@typescript/native": "npm:typescript@^7.0.2", + "eslint": "^10.9.1", + "tsup": "^8.5.1", + "tsx": "^4.23.12", + "typescript": "npm:@typescript/typescript6@^6.0.2", + "vitest": "^4.1.11" + }, + "engines": { + "node": "24.x" + }, + "packageManager": "pnpm@11.24.0", + "publishConfig": { + "access": "public" + } +} diff --git a/tools/create-basilic/src/assemble/agent-reads.test.ts b/tools/create-basilic/src/assemble/agent-reads.test.ts new file mode 100644 index 00000000..a7551b88 --- /dev/null +++ b/tools/create-basilic/src/assemble/agent-reads.test.ts @@ -0,0 +1,38 @@ +import { existsSync } from 'node:fs' +import { mkdtemp } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { repoRootFromPackage } from '../paths.js' +import { classifyPath, loadManifest } from './classify.js' +import { resetFirstInstance } from './first-reset.js' +import { agentReadPaths } from './index.js' +import { snapshotDocs } from './snapshot-docs.js' + +describe('assembled agent reads', () => { + it('keeps mandatory agent files in the classified tree', () => { + const manifest = loadManifest() + for (const path of agentReadPaths) { + if (path.startsWith('docs/basilic/') || path === '_first/PRODUCT.md') continue + expect(existsSync(join(repoRootFromPackage, path)), path).toBe(true) + expect(classifyPath({ path, manifest })?.kind).not.toBe('exclude') + } + }) + + it('snapshots at least the current docs page count', () => { + const destRoot = join(tmpdir(), `create-basilic-docs-${Date.now()}`) + const { pages } = snapshotDocs({ sourceRoot: repoRootFromPackage, destRoot }) + expect(pages).toBeGreaterThanOrEqual(40) + }) + + it('snapshots docs and writes an unfilled FIRST instance', async () => { + const dest = await mkdtemp(join(tmpdir(), 'create-basilic-first-')) + snapshotDocs({ sourceRoot: repoRootFromPackage, destRoot: dest }) + resetFirstInstance({ destRoot: dest }) + for (const path of agentReadPaths.filter( + item => item.startsWith('_first/') || item.startsWith('docs/basilic/'), + )) + expect(existsSync(join(dest, path)), path).toBe(true) + expect(existsSync(join(dest, '_first/basilic/PRODUCT.md'))).toBe(false) + }) +}) diff --git a/tools/create-basilic/src/assemble/classify.test.ts b/tools/create-basilic/src/assemble/classify.test.ts new file mode 100644 index 00000000..bf4c6368 --- /dev/null +++ b/tools/create-basilic/src/assemble/classify.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it } from 'vitest' +import { listTrackedFiles } from '../git.js' +import { repoRootFromPackage } from '../paths.js' +import { classifyPath, classifyTrackedFiles, loadManifest } from './classify.js' + +describe('classifyPath', () => { + const manifest = loadManifest() + + it('excludes the documentation app and generator', () => { + expect(classifyPath({ path: 'apps/docu/package.json', manifest })?.kind).toBe('exclude') + expect(classifyPath({ path: 'tools/create-basilic/src/cli.ts', manifest })?.kind).toBe( + 'exclude', + ) + }) + + it('includes portable tools and packages', () => { + expect(classifyPath({ path: 'tools/eslint/package.json', manifest })?.kind).toBe('include') + expect(classifyPath({ path: 'packages/core/src/index.ts', manifest })?.kind).toBe('include') + }) + + it('transforms agent and app surfaces', () => { + expect(classifyPath({ path: 'AGENTS.md', manifest })?.kind).toBe('transform') + expect(classifyPath({ path: 'apps/web/app/layout.tsx', manifest })?.kind).toBe('transform') + }) + + it('prefers exclude over a parent transform', () => { + expect(classifyPath({ path: '_first/basilic/PRODUCT.md', manifest })?.kind).toBe('exclude') + expect(classifyPath({ path: '_first/FIRST.md', manifest })?.kind).toBe('transform') + }) + + it('excludes release automation', () => { + expect(classifyPath({ path: 'release-please-config.json', manifest })?.kind).toBe('exclude') + expect(classifyPath({ path: '.github/workflows/release-please.yml', manifest })?.kind).toBe( + 'exclude', + ) + expect( + classifyPath({ path: '.github/workflows/publish-create-basilic.yml', manifest })?.kind, + ).toBe('exclude') + expect(classifyPath({ path: 'scripts/prepare-publish.mjs', manifest })?.kind).toBe('exclude') + expect(classifyPath({ path: 'CHANGELOG.md', manifest })?.kind).toBe('exclude') + }) +}) + +describe('classification completeness', () => { + it('classifies every tracked git path', () => { + const manifest = loadManifest() + const files = listTrackedFiles({ repoRoot: repoRootFromPackage }) + const { unclassified } = classifyTrackedFiles({ files, manifest }) + expect(unclassified, unclassified.join('\n')).toEqual([]) + }) +}) diff --git a/tools/create-basilic/src/assemble/classify.ts b/tools/create-basilic/src/assemble/classify.ts new file mode 100644 index 00000000..51f51832 --- /dev/null +++ b/tools/create-basilic/src/assemble/classify.ts @@ -0,0 +1,74 @@ +import { readFileSync } from 'node:fs' +import { manifestPath } from '../paths.js' + +export type Classification = 'include' | 'transform' | 'exclude' + +export type Manifest = { + exclude: string[] + transform: string[] + include: string[] +} + +export type ClassifiedPath = { + path: string + kind: Classification + rule: string +} + +export function loadManifest({ path = manifestPath }: { path?: string } = {}) { + return JSON.parse(readFileSync(path, 'utf8')) as Manifest +} + +export function matchesRule({ path, rule }: { path: string; rule: string }) { + const normalized = rule.endsWith('/') ? rule.slice(0, -1) : rule + return path === normalized || path.startsWith(`${normalized}/`) +} + +export function longestMatch({ path, rules }: { path: string; rules: string[] }) { + let best: string | undefined + for (const rule of rules) { + if (!matchesRule({ path, rule })) continue + if (!best || rule.length > best.length) best = rule + } + return best +} + +const kindRank = { exclude: 0, transform: 1, include: 2 } as const + +export function classifyPath({ path, manifest }: { path: string; manifest: Manifest }) { + const candidates = ( + [ + { kind: 'exclude', rule: longestMatch({ path, rules: manifest.exclude }) }, + { kind: 'transform', rule: longestMatch({ path, rules: manifest.transform }) }, + { kind: 'include', rule: longestMatch({ path, rules: manifest.include }) }, + ] as const + ).filter((row): row is { kind: Classification; rule: string } => Boolean(row.rule)) + + if (candidates.length === 0) return null + + candidates.sort((a, b) => { + const byLength = b.rule.length - a.rule.length + if (byLength !== 0) return byLength + return kindRank[a.kind] - kindRank[b.kind] + }) + + const winner = candidates[0] + if (!winner) return null + return { kind: winner.kind, rule: winner.rule } +} + +export function classifyTrackedFiles({ files, manifest }: { files: string[]; manifest: Manifest }) { + const classified: ClassifiedPath[] = [] + const unclassified: string[] = [] + + for (const path of files) { + const result = classifyPath({ path, manifest }) + if (!result) { + unclassified.push(path) + continue + } + classified.push({ path, kind: result.kind, rule: result.rule }) + } + + return { classified, unclassified } +} diff --git a/tools/create-basilic/src/assemble/first-reset.ts b/tools/create-basilic/src/assemble/first-reset.ts new file mode 100644 index 00000000..a3579889 --- /dev/null +++ b/tools/create-basilic/src/assemble/first-reset.ts @@ -0,0 +1,52 @@ +import { mkdirSync, rmSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' + +export function resetFirstInstance({ destRoot }: { destRoot: string }) { + rmSync(join(destRoot, '_first/basilic'), { recursive: true, force: true }) + mkdirSync(join(destRoot, '_first'), { recursive: true }) + writeFileSync(join(destRoot, '_first/FIRST.md'), firstMap) + writeFileSync(join(destRoot, '_first/PRODUCT.md'), productShell) +} + +const firstMap = `# FIRST + +spec: 0.3-draft + +This repository’s instance map. Absent files beat empty stubs. Do not copy another product’s overlay in. + +Durable product facts live in [PRODUCT.md](PRODUCT.md). Fill stations as you make durable decisions. The factory lives in [\`blockmatic/first\`](https://github.com/blockmatic/first). Technical starter docs: [\`docs/basilic/\`](../docs/basilic/). Hosted: [basilic-docs](https://basilic-docs.vercel.app/docs). + +## In + +- product: [PRODUCT.md](PRODUCT.md) + +## Out + +journeys, architecture, data, api, documentation, workflow, quality, security, operations — add files here when those stations are In. +` + +const productShell = `# Product First + +## Principle + +See /f-product. + +## Artifacts + +- **Unresolved:** Fill this overlay for the generated product. Do not inherit Basilic’s roadmap. + +## Minimum Useful Artifact + +- problem: (unresolved) +- users: (unresolved) +- goal: (unresolved) +- non-goals: (unresolved) +- audience/channel/first use: \`pnpm setup\` → \`pnpm --filter @repo/api db:start\` → \`pnpm reset\` → \`pnpm dev\` +- metrics: (unresolved) +- events: (unresolved) +- owners: (unresolved) + +## Notes + +Product names what, why, and how we will know. Do not write product intent into \`docs/basilic/\`. +` diff --git a/tools/create-basilic/src/assemble/fixture.test.ts b/tools/create-basilic/src/assemble/fixture.test.ts new file mode 100644 index 00000000..3fba391b --- /dev/null +++ b/tools/create-basilic/src/assemble/fixture.test.ts @@ -0,0 +1,141 @@ +import { createHash } from 'node:crypto' +import { readFileSync } from 'node:fs' +import { mkdtemp } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { classifyPath, loadManifest } from './classify.js' +import { resetFirstInstance } from './first-reset.js' + +const sentinelPaths = [ + ['LICENSE', 'include'], + ['package.json', 'transform'], + ['pnpm-lock.yaml', 'include'], + ['AGENTS.md', 'transform'], + ['apps/api/package.json', 'transform'], + ['apps/web/package.json', 'transform'], + ['apps/mobile/app.json', 'transform'], + ['apps/docu/package.json', 'exclude'], + ['packages/cli/package.json', 'transform'], + ['tools/eslint/package.json', 'include'], + ['tools/typescript/package.json', 'include'], + ['tools/create-basilic/package.json', 'exclude'], + ['_first/FIRST.md', 'transform'], + ['_first/basilic/PRODUCT.md', 'exclude'], + ['scripts/run-qa.mjs', 'transform'], + ['scripts/prepare-publish.mjs', 'exclude'], + ['release-please-config.json', 'exclude'], + ['.github/workflows/lint.yml', 'transform'], + ['.github/workflows/release-please.yml', 'exclude'], + ['.github/workflows/publish-create-basilic.yml', 'exclude'], + ['CHANGELOG.md', 'exclude'], +] as const + +function sha256(value: Buffer) { + return createHash('sha256').update(value).digest('hex') +} + +describe('exact-version fixture', () => { + it('pins include/transform/exclude prefixes', () => { + const manifest = loadManifest() + expect(manifest).toEqual({ + exclude: [ + 'apps/docu/', + 'tools/create-basilic/', + 'scripts/prepare-publish.mjs', + 'scripts/restore-publish.mjs', + 'scripts/release-impact.mjs', + 'scripts/assert-generated-tree.mjs', + '_first/basilic/', + '_first/templates/', + '.github/workflows/pr-title.yml', + '.github/workflows/scaffold.yml', + '.github/workflows/release-please.yml', + '.github/workflows/publish-create-basilic.yml', + '.github/workflows/release-impact.yml', + '.github/workflows/scaffold-acceptance.yml', + '.github/PULL_REQUEST_TEMPLATE.md', + 'release-please-config.json', + '.release-please-manifest.json', + 'CHANGELOG.md', + '__dev/', + ], + transform: [ + 'package.json', + 'README.md', + 'AGENTS.md', + 'CLAUDE.md', + 'turbo.json', + 'skills-lock.json', + '.coderabbit.yaml', + '.cursor/', + '.agents/', + '_first/', + '.deepsec/', + 'scripts/run-qa.mjs', + 'scripts/README.md', + 'apps/api/', + 'apps/web/', + 'apps/mobile/', + 'packages/cli/', + '.github/workflows/', + ], + include: [ + 'apps/api/', + 'apps/web/', + 'apps/mobile/', + 'packages/', + 'tools/eslint/', + 'tools/typescript/', + 'scripts/', + '.github/', + '.cursor/', + '.agents/', + '.deepsec/', + '_first/', + '.vscode/', + 'AGENTS.md', + 'CLAUDE.md', + 'LICENSE', + 'README.md', + 'biome.json', + 'eslint.config.mjs', + 'osv-scanner.toml', + 'package.json', + 'pnpm-lock.yaml', + 'pnpm-workspace.yaml', + 'skills-lock.json', + 'tsconfig.json', + 'turbo.json', + '.gitignore', + '.gitleaks.toml', + '.node-version', + '.nvmrc', + '.trufflehogignore', + '.coderabbit.yaml', + ], + }) + }) + + it('pins classification of key paths', () => { + const manifest = loadManifest() + expect( + sentinelPaths.map(([path, kind]) => ({ + path, + kind: classifyPath({ path, manifest })?.kind, + expected: kind, + })), + ).toEqual(sentinelPaths.map(([path, kind]) => ({ path, kind, expected: kind }))) + }) + + it('pins hashes of generated FIRST shells', async () => { + const destRoot = await mkdtemp(join(tmpdir(), 'create-basilic-fixture-')) + resetFirstInstance({ destRoot }) + expect(sha256(readFileSync(join(destRoot, '_first/FIRST.md')))).toBe( + 'aaf6bb69aee5fbae5a20436d42dd3fbc34867db9ecf8e0b2bbd309a1b9fdb782', + ) + expect(sha256(readFileSync(join(destRoot, '_first/PRODUCT.md')))).toBe( + 'ba8b11a8e9bef37aab1f835af3eb0824126a088e92420f6814273b5b2d39f766', + ) + }) +}) diff --git a/tools/create-basilic/src/assemble/index.ts b/tools/create-basilic/src/assemble/index.ts new file mode 100644 index 00000000..77223c38 --- /dev/null +++ b/tools/create-basilic/src/assemble/index.ts @@ -0,0 +1,152 @@ +import { cpSync, existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { dirname, join, resolve, sep } from 'node:path' +import { digestTree } from '../digest.js' +import { + assertCleanWorktree, + extractHeadArchive, + gitHeadSha, + listTrackedFiles, + listUntrackedFiles, +} from '../git.js' +import { classifyTrackedFiles, loadManifest } from './classify.js' +import { resetFirstInstance } from './first-reset.js' +import { regenerateLockfile } from './lockfile.js' +import { rewritePointers } from './pointers.js' +import { snapshotDocs } from './snapshot-docs.js' +import { applyAssembleTransforms } from './transforms.js' + +export const forbiddenGeneratedPaths = [ + 'apps/docu', + 'tools/create-basilic', + 'scripts/prepare-publish.mjs', + 'scripts/restore-publish.mjs', + '_first/basilic', + 'release-please-config.json', + '.release-please-manifest.json', + '.github/workflows/release-please.yml', + '.github/workflows/publish-create-basilic.yml', +] + +export const agentReadPaths = [ + 'AGENTS.md', + '.cursor/rules/base/general.mdc', + '.cursor/rules/base/file-organization.mdc', + '.cursor/rules/base/naming.mdc', + '.cursor/rules/base/docs.mdc', + '.agents/skills/b/SKILL.md', + '.agents/skills/b/b-exec-push/SKILL.md', + '.agents/skills/b/b-git-commit/SKILL.md', + '_first/FIRST.md', + '_first/PRODUCT.md', + 'docs/basilic/development/index.md', + 'docs/basilic/development/file-organization.md', + 'docs/basilic/testing/product-ready.md', +] + +export function assembleTemplate({ + repoRoot, + dest, + lockfile = false, + allowDirty = false, +}: { + repoRoot: string + dest: string + lockfile?: boolean + allowDirty?: boolean +}) { + if (!allowDirty) assertCleanWorktree({ repoRoot }) + + const manifest = loadManifest() + const tracked = listTrackedFiles({ repoRoot }) + const files = allowDirty + ? [...new Set([...tracked, ...listUntrackedFiles({ repoRoot })])] + : tracked + const { classified, unclassified } = classifyTrackedFiles({ files, manifest }) + if (unclassified.length > 0) { + const preview = unclassified.slice(0, 20).join('\n') + throw new Error( + `Unclassified tracked paths (${unclassified.length}). Add include/transform/exclude rules.\n${preview}`, + ) + } + + mkdirSync(dest, { recursive: true }) + + if (allowDirty) { + copyClassifiedFromWorktree({ repoRoot, dest, classified }) + } else { + extractHeadArchive({ repoRoot, dest }) + snapshotDocs({ sourceRoot: dest, destRoot: dest }) + for (const { path, kind } of classified) { + if (kind !== 'exclude') continue + rmSync(join(dest, path), { recursive: true, force: true }) + } + } + + if (allowDirty) snapshotDocs({ sourceRoot: repoRoot, destRoot: dest }) + + applyAssembleTransforms({ destRoot: dest }) + resetFirstInstance({ destRoot: dest }) + rewritePointers({ destRoot: dest }) + if (lockfile) regenerateLockfileOutsideWorkspace({ dest, repoRoot }) + + const digest = digestTree({ root: dest }) + const sourceSha = gitHeadSha({ repoRoot }) + writeFileSync( + join(dest, '.basilic-template.json'), + `${JSON.stringify( + { + sourceSha, + digest, + files: classified.filter(row => row.kind !== 'exclude').length, + }, + null, + 2, + )}\n`, + ) + + return { digest, sourceSha, classified, unclassified } +} + +function regenerateLockfileOutsideWorkspace({ + dest, + repoRoot, +}: { + dest: string + repoRoot: string +}) { + const destResolved = resolve(dest) + const repoResolved = resolve(repoRoot) + const nested = destResolved === repoResolved || destResolved.startsWith(`${repoResolved}${sep}`) + if (!nested) { + regenerateLockfile({ destRoot: dest }) + return + } + const staging = join(tmpdir(), `create-basilic-lock-${process.pid}`) + rmSync(staging, { recursive: true, force: true }) + cpSync(dest, staging, { recursive: true }) + regenerateLockfile({ destRoot: staging }) + rmSync(dest, { recursive: true, force: true }) + mkdirSync(dirname(dest), { recursive: true }) + cpSync(staging, dest, { recursive: true }) + rmSync(staging, { recursive: true, force: true }) +} + +function copyClassifiedFromWorktree({ + repoRoot, + dest, + classified, +}: { + repoRoot: string + dest: string + classified: { path: string; kind: string }[] +}) { + for (const { path, kind } of classified) { + if (kind === 'exclude') continue + const from = join(repoRoot, path) + if (!existsSync(from)) continue + const to = join(dest, path) + mkdirSync(dirname(to), { recursive: true }) + cpSync(from, to) + } +} diff --git a/tools/create-basilic/src/assemble/lockfile.ts b/tools/create-basilic/src/assemble/lockfile.ts new file mode 100644 index 00000000..ba043162 --- /dev/null +++ b/tools/create-basilic/src/assemble/lockfile.ts @@ -0,0 +1,19 @@ +import { spawnSync } from 'node:child_process' +import { existsSync } from 'node:fs' + +export function regenerateLockfile({ destRoot }: { destRoot: string }) { + if (!existsSync(destRoot)) throw new Error(`Assembled tree missing: ${destRoot}`) + const childEnv = { ...process.env } + Reflect.set(childEnv, 'CI', '1') + const result = spawnSync( + 'pnpm', + ['install', '--lockfile-only', '--ignore-scripts', '--config.engine-strict=true'], + { + cwd: destRoot, + stdio: 'inherit', + env: childEnv, + }, + ) + if (result.status !== 0) + throw new Error(`pnpm lockfile regeneration failed (exit ${result.status ?? 1})`) +} diff --git a/tools/create-basilic/src/assemble/pointers.test.ts b/tools/create-basilic/src/assemble/pointers.test.ts new file mode 100644 index 00000000..1ee669c3 --- /dev/null +++ b/tools/create-basilic/src/assemble/pointers.test.ts @@ -0,0 +1,20 @@ +import { describe, expect, it } from 'vitest' +import { rewriteFilePointers } from './pointers.js' + +describe('rewriteFilePointers', () => { + it('rewrites canonical docs and FIRST pointers', () => { + const next = rewriteFilePointers({ + path: 'AGENTS.md', + content: [ + 'Read [`apps/docu/content/docs/`](apps/docu/content/docs/) ', + 'and `_first/basilic/PRODUCT.md`.', + 'Also ../docu/content/docs/testing/product-ready.mdx', + ].join('\n'), + }) + expect(next).toContain('docs/basilic/') + expect(next).toContain('_first/PRODUCT.md') + expect(next).not.toContain('apps/docu/content/docs/') + expect(next).not.toContain('_first/basilic/') + expect(next).toContain('docs/basilic/testing/product-ready.md') + }) +}) diff --git a/tools/create-basilic/src/assemble/pointers.ts b/tools/create-basilic/src/assemble/pointers.ts new file mode 100644 index 00000000..6a0dbc11 --- /dev/null +++ b/tools/create-basilic/src/assemble/pointers.ts @@ -0,0 +1,54 @@ +import { readFileSync, writeFileSync } from 'node:fs' +import { dirname, join, relative } from 'node:path' +import { listFiles } from '../digest.js' + +const pointerGlobs = [ + 'AGENTS.md', + 'CLAUDE.md', + '.cursor/rules/', + '.agents/', + 'apps/', + 'packages/', + 'scripts/', + '_first/', +] + +const skipRewrite = /\.(png|jpe?g|gif|webp|ico|woff2?|ttf|eot|zip|gz|tgz|wasm|mp4|sqlite3?)$/i + +export function rewritePointers({ destRoot }: { destRoot: string }) { + for (const path of listFiles({ root: destRoot })) { + if (!shouldRewrite({ path })) continue + const abs = join(destRoot, path) + const original = readFileSync(abs, 'utf8') + const next = rewriteFilePointers({ path, content: original }) + if (next !== original) writeFileSync(abs, next) + } +} + +export function rewriteFilePointers({ path, content }: { path: string; content: string }) { + let next = content + next = next.replaceAll('apps/docu/content/docs/', 'docs/basilic/') + next = next.replaceAll('@apps/docu/content/docs/', 'docs/basilic/') + next = next.replaceAll('apps/docu/content/docs', 'docs/basilic') + next = next.replaceAll('_first/basilic/PRODUCT.md', '_first/PRODUCT.md') + next = next.replaceAll('_first/basilic/', '_first/') + next = next.replaceAll('globs: "apps/docu/**/*.mdx"', 'globs: "docs/basilic/**/*.md"') + next = rewriteRelativeDocuLinks({ path, content: next }) + return next +} + +function shouldRewrite({ path }: { path: string }) { + if (path.startsWith('docs/basilic/')) return false + if (skipRewrite.test(path)) return false + return pointerGlobs.some(rule => path === rule || path.startsWith(rule) || path.endsWith(rule)) +} + +function rewriteRelativeDocuLinks({ path, content }: { path: string; content: string }) { + return content.replace( + /(\.\.\/)+docu\/content\/docs\/([^\s)"'`]+)/g, + (_full, _dots, slug: string) => { + const target = `docs/basilic/${slug.replace(/\.mdx$/, '.md')}` + return relative(dirname(path), target).replaceAll('\\', '/') + }, + ) +} diff --git a/tools/create-basilic/src/assemble/run.ts b/tools/create-basilic/src/assemble/run.ts new file mode 100644 index 00000000..4c73a2bc --- /dev/null +++ b/tools/create-basilic/src/assemble/run.ts @@ -0,0 +1,20 @@ +#!/usr/bin/env node + +import { rmSync } from 'node:fs' +import { bundledTemplateRoot, repoRootFromPackage } from '../paths.js' +import { assembleTemplate } from './index.js' + +const lockfile = process.env.CREATE_BASILIC_LOCKFILE === '1' +const allowDirty = process.env.CREATE_BASILIC_ALLOW_DIRTY === '1' +const dest = process.env.CREATE_BASILIC_TEMPLATE_DIR ?? bundledTemplateRoot + +rmSync(dest, { recursive: true, force: true }) +const result = assembleTemplate({ + repoRoot: repoRootFromPackage, + dest, + lockfile, + allowDirty, +}) +process.stderr.write( + `Assembled template at ${dest}\nSHA ${result.sourceSha}\ndigest ${result.digest}\n`, +) diff --git a/tools/create-basilic/src/assemble/snapshot-docs.test.ts b/tools/create-basilic/src/assemble/snapshot-docs.test.ts new file mode 100644 index 00000000..58b1b53a --- /dev/null +++ b/tools/create-basilic/src/assemble/snapshot-docs.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' +import { mdxToMarkdown } from './snapshot-docs.js' + +describe('mdxToMarkdown', () => { + it('strips frontmatter and rewrites local doc links', () => { + const markdown = mdxToMarkdown({ + source: `--- +title: "Getting Started" +description: "Clone and run." +--- + +See [After fork](/docs/development/after-fork) and [Product Ready](/docs/testing/product-ready). +`, + slug: 'development/index', + outputSlugs: new Set(['development/after-fork.md', 'testing/product-ready.md']), + }) + expect(markdown).toContain('# Getting Started') + expect(markdown).toContain('Clone and run.') + expect(markdown).toContain('](after-fork.md)') + expect(markdown).toContain('](../testing/product-ready.md)') + expect(markdown).not.toContain('---') + }) +}) diff --git a/tools/create-basilic/src/assemble/snapshot-docs.ts b/tools/create-basilic/src/assemble/snapshot-docs.ts new file mode 100644 index 00000000..20c135d4 --- /dev/null +++ b/tools/create-basilic/src/assemble/snapshot-docs.ts @@ -0,0 +1,119 @@ +import { mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs' +import { dirname, join, posix } from 'node:path' + +const docsHost = 'https://basilic-docs.vercel.app/docs' + +export function snapshotDocs({ sourceRoot, destRoot }: { sourceRoot: string; destRoot: string }) { + const mdxRoot = join(sourceRoot, 'apps/docu/content/docs') + const outRoot = join(destRoot, 'docs/basilic') + const mdxFiles = listMdx({ dir: mdxRoot }) + const outputSlugs = new Set(mdxFiles.map(file => file.replace(/\.mdx$/, '.md'))) + + for (const file of mdxFiles) { + const source = readFileSync(join(mdxRoot, file), 'utf8') + const markdown = mdxToMarkdown({ + source, + slug: file.replace(/\.mdx$/, ''), + outputSlugs, + }) + const dest = join(outRoot, file.replace(/\.mdx$/, '.md')) + mkdirSync(dirname(dest), { recursive: true }) + writeFileSync(dest, markdown) + } + + return { pages: mdxFiles.length } +} + +function listMdx({ dir, prefix = '' }: { dir: string; prefix?: string }): string[] { + const entries = readdirSync(join(dir, prefix), { withFileTypes: true }) + const files: string[] = [] + for (const entry of entries) { + const relativePath = prefix ? `${prefix}/${entry.name}` : entry.name + if (entry.isDirectory()) { + files.push(...listMdx({ dir, prefix: relativePath })) + continue + } + if (entry.isFile() && entry.name.endsWith('.mdx')) files.push(relativePath) + } + return files.sort((left, right) => left.localeCompare(right)) +} + +export function mdxToMarkdown({ + source, + slug, + outputSlugs, +}: { + source: string + slug: string + outputSlugs: Set +}) { + const { title, description, body } = splitFrontmatter(source) + let markdown = body.replace(/^import\s.+;?\s*$/gm, '') + markdown = rewriteHrefAndMarkdownLinks({ markdown, slug, outputSlugs }) + markdown = stripJsx(markdown) + const heading = title ? `# ${title}\n\n` : '' + const lead = description ? `${description}\n\n` : '' + return `${heading}${lead}${markdown.trim()}\n` +} + +function splitFrontmatter(source: string) { + if (!source.startsWith('---\n')) return { title: undefined, description: undefined, body: source } + const end = source.indexOf('\n---\n', 4) + if (end === -1) return { title: undefined, description: undefined, body: source } + const raw = source.slice(4, end) + const body = source.slice(end + 5) + const title = raw.match(/^title:\s*"?([^"\n]+)"?\s*$/m)?.[1] + const description = raw.match(/^description:\s*"?([^"\n]+)"?\s*$/m)?.[1] + return { title, description, body } +} + +function rewriteHrefAndMarkdownLinks({ + markdown, + slug, + outputSlugs, +}: { + markdown: string + slug: string + outputSlugs: Set +}) { + const fromDir = dirname(`docs/basilic/${slug}.md`) + const toLocal = (targetSlug: string) => { + const local = resolveLocalDoc({ targetSlug, outputSlugs }) + if (!local) return `${docsHost}/${targetSlug}` + return posix.relative(fromDir, `docs/basilic/${local}`) || posix.basename(local) + } + + return markdown + .replaceAll(/\]\(\/docs\/([a-z0-9\-_/]+)\)/g, (_full, targetSlug: string) => { + return `](${toLocal(targetSlug)})` + }) + .replaceAll(/href="\/docs\/([a-z0-9\-_/]+)"/g, (_full, targetSlug: string) => { + return `href="${toLocal(targetSlug)}"` + }) +} + +function resolveLocalDoc({ + targetSlug, + outputSlugs, +}: { + targetSlug: string + outputSlugs: Set +}) { + const candidates = [`${targetSlug}.md`, `${targetSlug}/index.md`] + return candidates.find(candidate => outputSlugs.has(candidate)) +} + +function stripJsx(markdown: string) { + return markdown + .replace(//g, '') + .replace(//g, '') + .replace(/<\/?[A-Z][A-Za-z0-9]*\b[^>]*>/g, '') + .replace(/]*>/g, '') + .replace(/<\/div>/g, '') + .replace(/]*>/g, '') + .replace(/<\/p>/g, '\n\n') + .replace(/]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g, '[$2]($1)') + .replace(/]*>([\s\S]*?)<\/h3>/g, '### $1\n') + .replace(/]*>([\s\S]*?)<\/span>/g, '$1') + .replace(/\n{3,}/g, '\n\n') +} diff --git a/tools/create-basilic/src/assemble/transforms.test.ts b/tools/create-basilic/src/assemble/transforms.test.ts new file mode 100644 index 00000000..0875e611 --- /dev/null +++ b/tools/create-basilic/src/assemble/transforms.test.ts @@ -0,0 +1,29 @@ +import { readFileSync, writeFileSync } from 'node:fs' +import { mkdtemp } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { applyAssembleTransforms } from './transforms.js' + +describe('applyAssembleTransforms', () => { + it('rewrites local skills-lock sources to GitHub', async () => { + const destRoot = await mkdtemp(join(tmpdir(), 'create-basilic-lock-')) + writeFileSync(join(destRoot, 'turbo.json'), '{"tasks":{"@repo/docu#build":{}}}\n') + writeFileSync( + join(destRoot, 'skills-lock.json'), + JSON.stringify({ + version: 1, + skills: { + b: { source: '../basilic-skills', sourceType: 'local' }, + }, + }), + ) + applyAssembleTransforms({ destRoot }) + const lock = JSON.parse(readFileSync(join(destRoot, 'skills-lock.json'), 'utf8')) as { + skills: { b: { source: string; sourceType: string; skillPath: string } } + } + expect(lock.skills.b.source).toBe('blockmatic/basilic-skills') + expect(lock.skills.b.sourceType).toBe('github') + expect(lock.skills.b.skillPath).toBe('skills/b/SKILL.md') + }) +}) diff --git a/tools/create-basilic/src/assemble/transforms.ts b/tools/create-basilic/src/assemble/transforms.ts new file mode 100644 index 00000000..9cf36143 --- /dev/null +++ b/tools/create-basilic/src/assemble/transforms.ts @@ -0,0 +1,67 @@ +import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' + +export function applyAssembleTransforms({ destRoot }: { destRoot: string }) { + dropDocuTurboTask({ destRoot }) + stripVercelMcp({ destRoot }) + placeholderDeepsec({ destRoot }) + dropDocuCoderabbitPath({ destRoot }) + rewriteLocalSkillSources({ destRoot }) +} + +function dropDocuTurboTask({ destRoot }: { destRoot: string }) { + const path = join(destRoot, 'turbo.json') + const turbo = JSON.parse(readFileSync(path, 'utf8')) as { + tasks: Record + } + delete turbo.tasks['@repo/docu#build'] + writeFileSync(path, `${JSON.stringify(turbo, null, 2)}\n`) +} + +function stripVercelMcp({ destRoot }: { destRoot: string }) { + const path = join(destRoot, '.cursor/mcp.json') + if (!existsSync(path)) return + const mcp = JSON.parse(readFileSync(path, 'utf8')) as { + mcpServers: Record + } + for (const [name, server] of Object.entries(mcp.mcpServers)) + if (typeof server.url === 'string' && /basilic-(docu|fastify|next)/.test(server.url)) + delete mcp.mcpServers[name] + writeFileSync(path, `${JSON.stringify(mcp, null, 2)}\n`) +} + +function placeholderDeepsec({ destRoot }: { destRoot: string }) { + const path = join(destRoot, '.deepsec/deepsec.config.ts') + if (!existsSync(path)) return + const source = readFileSync(path, 'utf8') + .replace('id: "basilic"', 'id: "app"') + .replace( + 'githubUrl: "https://github.com/blockmatic/basilic/blob/main"', + 'githubUrl: "https://github.com/example/app/blob/main"', + ) + writeFileSync(path, source) + const dataDir = join(destRoot, '.deepsec/data/basilic') + if (existsSync(dataDir)) rmSync(dataDir, { recursive: true, force: true }) +} + +function dropDocuCoderabbitPath({ destRoot }: { destRoot: string }) { + const path = join(destRoot, '.coderabbit.yaml') + if (!existsSync(path)) return + const source = readFileSync(path, 'utf8').replace(/^\s*- path: "apps\/docu\/\*\*"\n/m, '') + writeFileSync(path, source) +} + +function rewriteLocalSkillSources({ destRoot }: { destRoot: string }) { + const path = join(destRoot, 'skills-lock.json') + if (!existsSync(path)) return + const lock = JSON.parse(readFileSync(path, 'utf8')) as { + skills?: Record + } + for (const [name, skill] of Object.entries(lock.skills ?? {})) { + if (skill.sourceType !== 'local') continue + skill.source = 'blockmatic/basilic-skills' + skill.sourceType = 'github' + if (!skill.skillPath) skill.skillPath = `skills/${name}/SKILL.md` + } + writeFileSync(path, `${JSON.stringify(lock, null, 2)}\n`) +} diff --git a/tools/create-basilic/src/cli.ts b/tools/create-basilic/src/cli.ts new file mode 100644 index 00000000..23514308 --- /dev/null +++ b/tools/create-basilic/src/cli.ts @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +import { readFileSync } from 'node:fs' +import { Command } from 'commander' +import { IoError } from './copy.js' +import { exitCodes } from './exit-codes.js' +import { generateProject } from './generate.js' +import { ValidationError } from './project-name.js' + +const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as { + version: string +} + +const program = new Command() + +program + .name('create-basilic') + .description('Scaffold an independent Basilic API, web, and mobile monorepo') + .version(pkg.version) + .argument('', 'Destination directory (created; must be empty)') + .option('-y, --yes', 'Accept safe defaults (never overwrites)') + .action(async (directory: string, options: { yes?: boolean }) => { + try { + const result = await generateProject({ + directory, + yes: Boolean(options.yes), + generatorVersion: pkg.version, + }) + process.stdout.write(`Created ${result.name.displayName} at ${result.dest}\n\n`) + process.stdout.write('Next:\n') + process.stdout.write(` cd ${directory}\n`) + process.stdout.write(' pnpm setup\n') + process.stdout.write(' pnpm --filter @repo/api db:start\n') + process.stdout.write(' pnpm reset\n') + process.stdout.write(' pnpm dev\n\n') + process.stdout.write( + 'Product Ready: https://basilic-docs.vercel.app/docs/testing/product-ready\n', + ) + } catch (error) { + const message = error instanceof Error ? error.message : String(error) + process.stderr.write(`${message}\n`) + if (error instanceof ValidationError) process.exit(error.exitCode) + if (error instanceof IoError) process.exit(error.exitCode) + process.exit(exitCodes.io) + } + }) + +program.parseAsync(process.argv).catch(error => { + process.stderr.write(`${error instanceof Error ? error.message : error}\n`) + process.exit(exitCodes.io) +}) + +process.on('SIGINT', () => { + process.stderr.write('\nInterrupted\n') + process.exit(exitCodes.interrupt) +}) diff --git a/tools/create-basilic/src/copy.test.ts b/tools/create-basilic/src/copy.test.ts new file mode 100644 index 00000000..c3d65ae2 --- /dev/null +++ b/tools/create-basilic/src/copy.test.ts @@ -0,0 +1,40 @@ +import { mkdirSync, writeFileSync } from 'node:fs' +import { mkdtemp, symlink } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { assertEmptyDest, copyTree, IoError } from './copy.js' + +describe('copyTree', () => { + it('refuses symlinks', async () => { + const root = await mkdtemp(join(tmpdir(), 'create-basilic-copy-')) + writeFileSync(join(root, 'file.txt'), 'ok') + await symlink(join(root, 'file.txt'), join(root, 'link.txt')) + await expect(copyTree({ from: root, to: join(root, 'out') })).rejects.toThrow(IoError) + }) + + it('copies paths with spaces', async () => { + const root = await mkdtemp(join(tmpdir(), 'create-basilic-copy-')) + const from = join(root, 'from dir') + mkdirSync(from) + writeFileSync(join(from, 'hello.txt'), 'hi') + const to = join(root, 'to dir') + await copyTree({ from, to }) + const { readFileSync } = await import('node:fs') + expect(readFileSync(join(to, 'hello.txt'), 'utf8')).toBe('hi') + }) +}) + +describe('assertEmptyDest', () => { + it('rejects a non-empty directory', async () => { + const dest = await mkdtemp(join(tmpdir(), 'create-basilic-dest-')) + writeFileSync(join(dest, 'keep.txt'), 'nope') + await expect(assertEmptyDest({ dest })).rejects.toThrow(/not empty/) + }) + + it('rejects an existing file', async () => { + const dest = join(await mkdtemp(join(tmpdir(), 'create-basilic-file-')), 'out.txt') + writeFileSync(dest, 'nope') + await expect(assertEmptyDest({ dest })).rejects.toThrow(/not an empty directory/) + }) +}) diff --git a/tools/create-basilic/src/copy.ts b/tools/create-basilic/src/copy.ts new file mode 100644 index 00000000..9386125d --- /dev/null +++ b/tools/create-basilic/src/copy.ts @@ -0,0 +1,76 @@ +import { copyFile, lstat, mkdir, readdir, rename, rm } from 'node:fs/promises' +import { dirname, join, relative, resolve, sep } from 'node:path' +import { exitCodes } from './exit-codes.js' + +export class IoError extends Error { + exitCode = exitCodes.io +} + +export async function isEmptyDir({ path }: { path: string }) { + try { + const entries = await readdir(path) + return entries.length === 0 + } catch (error) { + if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true + throw error + } +} + +export async function assertEmptyDest({ dest }: { dest: string }) { + try { + const stat = await lstat(dest) + if (stat.isFile() || stat.isSymbolicLink()) + throw new IoError(`Destination "${dest}" exists and is not an empty directory.`) + } catch (error) { + if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error + } + const empty = await isEmptyDir({ path: dest }) + if (!empty) + throw new IoError( + `Destination "${dest}" already exists and is not empty. Refusing to overwrite.`, + ) +} + +export async function copyTree({ + from, + to, + root = from, +}: { + from: string + to: string + root?: string +}) { + const resolvedFrom = resolve(from) + const resolvedTo = resolve(to) + const rel = relative(root, resolvedFrom) + if (rel.startsWith(`..${sep}`) || rel === '..') + throw new IoError(`Path escaped template root: ${resolvedFrom}`) + + const stat = await lstat(resolvedFrom) + if (stat.isSymbolicLink()) throw new IoError(`Refusing to copy symlink "${rel || resolvedFrom}"`) + + if (stat.isDirectory()) { + await mkdir(resolvedTo, { recursive: true }) + const entries = await readdir(resolvedFrom) + for (const name of entries) + await copyTree({ from: join(resolvedFrom, name), to: join(resolvedTo, name), root }) + return + } + + if (!stat.isFile()) throw new IoError(`Refusing to copy special file "${rel}"`) + + await mkdir(dirname(resolvedTo), { recursive: true }) + await copyFile(resolvedFrom, resolvedTo) +} + +export async function moveAtomic({ from, to }: { from: string; to: string }) { + await mkdir(dirname(to), { recursive: true }) + try { + await rename(from, to) + } catch (error) { + const code = (error as NodeJS.ErrnoException).code + if (code !== 'EXDEV' && code !== 'EPERM') throw error + await copyTree({ from, to }) + await rm(from, { recursive: true, force: true }) + } +} diff --git a/tools/create-basilic/src/digest.ts b/tools/create-basilic/src/digest.ts new file mode 100644 index 00000000..cfc60c57 --- /dev/null +++ b/tools/create-basilic/src/digest.ts @@ -0,0 +1,30 @@ +import { createHash } from 'node:crypto' +import { readdirSync, readFileSync } from 'node:fs' +import { join } from 'node:path' + +export function digestTree({ root }: { root: string }) { + const hash = createHash('sha256') + for (const path of listFiles({ root })) { + hash.update(path) + hash.update('\0') + hash.update(readFileSync(join(root, path))) + hash.update('\0') + } + return hash.digest('hex') +} + +export function listFiles({ root, prefix = '' }: { root: string; prefix?: string }): string[] { + const entries = readdirSync(join(root, prefix), { withFileTypes: true }).sort((left, right) => + left.name.localeCompare(right.name), + ) + const files: string[] = [] + for (const entry of entries) { + const relative = prefix ? `${prefix}/${entry.name}` : entry.name + if (entry.isDirectory()) { + files.push(...listFiles({ root, prefix: relative })) + continue + } + if (entry.isFile()) files.push(relative) + } + return files +} diff --git a/tools/create-basilic/src/exit-codes.ts b/tools/create-basilic/src/exit-codes.ts new file mode 100644 index 00000000..5cfde3d6 --- /dev/null +++ b/tools/create-basilic/src/exit-codes.ts @@ -0,0 +1,5 @@ +export const exitCodes = { + validation: 1, + io: 2, + interrupt: 130, +} diff --git a/tools/create-basilic/src/generate.test.ts b/tools/create-basilic/src/generate.test.ts new file mode 100644 index 00000000..c14784da --- /dev/null +++ b/tools/create-basilic/src/generate.test.ts @@ -0,0 +1,69 @@ +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { mkdtemp } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { IoError } from './copy.js' +import { generateProject } from './generate.js' + +async function miniTemplate() { + const root = await mkdtemp(join(tmpdir(), 'create-basilic-template-')) + writeFileSync(join(root, 'package.json'), '{"name":"basilic","private":true}\n') + writeFileSync(join(root, '.basilic-template.json'), '{"sourceSha":"abc","digest":"def"}\n') + mkdirSync(join(root, 'apps/web/app/auth/login'), { recursive: true }) + writeFileSync( + join(root, 'apps/web/app/auth/login/login-form.tsx'), + '

Welcome to Basilic

\n', + ) + return root +} + +describe('generateProject', () => { + it('creates a named project in a path with spaces', async () => { + const templateRoot = await miniTemplate() + const parent = await mkdtemp(join(tmpdir(), 'create-basilic-out-')) + const directory = join(parent, 'My App') + const result = await generateProject({ + directory, + yes: true, + templateRoot, + generatorVersion: '0.0.0-test', + }) + expect(result.name.slug).toBe('my-app') + const pkg = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8')) as { + name: string + basilic: { generatorVersion: string; templateDigest: string } + } + expect(pkg.name).toBe('my-app') + expect(pkg.basilic.generatorVersion).toBe('0.0.0-test') + expect(pkg.basilic.templateDigest).toBe('def') + expect(readFileSync(join(directory, 'README.md'), 'utf8')).toContain('My App') + expect( + readFileSync(join(directory, 'apps/web/app/auth/login/login-form.tsx'), 'utf8'), + ).toContain('Welcome to My App') + }) + + it('refuses a non-empty destination', async () => { + const templateRoot = await miniTemplate() + const dest = await mkdtemp(join(tmpdir(), 'create-basilic-existing-')) + writeFileSync(join(dest, 'stale.txt'), 'nope') + await expect( + generateProject({ + directory: dest, + templateRoot, + generatorVersion: '0.0.0-test', + }), + ).rejects.toThrow(IoError) + }) + + it('refuses a missing bundled template', async () => { + const dest = join(await mkdtemp(join(tmpdir(), 'create-basilic-missing-')), 'app') + await expect( + generateProject({ + directory: dest, + templateRoot: await mkdtemp(join(tmpdir(), 'create-basilic-empty-')), + generatorVersion: '0.0.0-test', + }), + ).rejects.toThrow(/Bundled template is missing/) + }) +}) diff --git a/tools/create-basilic/src/generate.ts b/tools/create-basilic/src/generate.ts new file mode 100644 index 00000000..1c0c051a --- /dev/null +++ b/tools/create-basilic/src/generate.ts @@ -0,0 +1,121 @@ +import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { mkdtemp } from 'node:fs/promises' +import { tmpdir } from 'node:os' +import { dirname, join, resolve } from 'node:path' +import { assertEmptyDest, copyTree, IoError, moveAtomic } from './copy.js' +import { bundledTemplateRoot } from './paths.js' +import { assertNode24, type ProjectName, parseProjectName } from './project-name.js' +import { applyProjectTransforms } from './transforms/index.js' + +export type GenerateInput = { + directory: string + yes?: boolean + templateRoot?: string + generatorVersion: string +} + +export async function generateProject({ + directory, + yes = false, + templateRoot = bundledTemplateRoot, + generatorVersion, +}: GenerateInput) { + assertNode24() + const name = parseProjectName({ directory }) + if (!existsSync(join(templateRoot, 'package.json'))) + throw new IoError( + 'Bundled template is missing. Assemble before packing, or pass templateRoot in tests.', + ) + const dest = resolve(directory) + await assertEmptyDest({ dest }) + if (existsSync(dest)) rmSync(dest, { recursive: true, force: true }) + + const parent = dirname(dest) + const tempParent = parent === dest ? tmpdir() : parent + const temp = await mkdtemp(join(tempParent, `.create-basilic-${name.slug}-`)) + + try { + await copyTree({ from: templateRoot, to: temp }) + applyProjectTransforms({ destRoot: temp, name }) + writeAdopterReadme({ destRoot: temp, name }) + writeProvenance({ destRoot: temp, name, generatorVersion, yes }) + rmSync(join(temp, '.basilic-template.json'), { force: true }) + await moveAtomic({ from: temp, to: dest }) + } catch (error) { + rmSync(temp, { recursive: true, force: true }) + if (error instanceof IoError) throw error + throw new IoError(error instanceof Error ? error.message : String(error)) + } + + return { dest, name } +} + +function writeProvenance({ + destRoot, + name, + generatorVersion, + yes, +}: { + destRoot: string + name: ProjectName + generatorVersion: string + yes: boolean +}) { + const path = join(destRoot, 'package.json') + const pkg = JSON.parse(readFileSync(path, 'utf8')) as { + basilic?: unknown + } + let templateSourceSha = 'unknown' + let templateDigest = 'unknown' + try { + const meta = JSON.parse(readFileSync(join(destRoot, '.basilic-template.json'), 'utf8')) as { + sourceSha: string + digest: string + } + templateSourceSha = meta.sourceSha + templateDigest = meta.digest + } catch { + // packed templates always include the meta file; tests may omit it + } + pkg.basilic = { + generatorVersion, + templateSourceSha, + templateDigest, + inputs: { directory: name.slug, yes }, + } + writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`) +} + +function writeAdopterReadme({ destRoot, name }: { destRoot: string; name: ProjectName }) { + writeFileSync(join(destRoot, 'README.md'), adopterReadme({ name })) +} + +function adopterReadme({ name }: { name: ProjectName }) { + return `# ${name.displayName} + +Generated with [create-basilic](https://www.npmjs.com/package/create-basilic). This tree is an independent monorepo (API, web, mobile). It does not include Basilic's documentation app or generator. + +## Setup + +Requires Node.js 24.x and pnpm 11.24.0. + +\`\`\`bash +pnpm setup +pnpm --filter @repo/api db:start +pnpm reset +pnpm dev +\`\`\` + +- API: http://localhost:3001 +- Web: http://localhost:3000 — first login \`test@test.ai\` (\`ALLOW_TEST=true\` in copied env) +- Mobile: \`pnpm --filter @repo/mobile start\` + +Local starter docs: [\`docs/basilic/\`](docs/basilic/). Hosted: [Product Ready](https://basilic-docs.vercel.app/docs/testing/product-ready). After you own the copy: [After fork](https://basilic-docs.vercel.app/docs/development/after-fork). + +## Customize + +Replace display names, mobile scheme (\`apps/mobile/app.json\`), DeepSec \`githubUrl\`, and Vercel project slugs in \`apps/web/next.config.mjs\`. Keep \`@repo/*\` package names. Fill \`_first/PRODUCT.md\`. + +Upstream contributions belong in a Basilic fork, not this generated repo. +` +} diff --git a/tools/create-basilic/src/git.ts b/tools/create-basilic/src/git.ts new file mode 100644 index 00000000..e57f0ac8 --- /dev/null +++ b/tools/create-basilic/src/git.ts @@ -0,0 +1,52 @@ +import { spawnSync } from 'node:child_process' + +export function listTrackedFiles({ repoRoot }: { repoRoot: string }) { + return gitZeroFiles({ repoRoot, args: ['ls-files', '-z'] }) +} + +export function listUntrackedFiles({ repoRoot }: { repoRoot: string }) { + return gitZeroFiles({ repoRoot, args: ['ls-files', '--others', '--exclude-standard', '-z'] }) +} + +function gitZeroFiles({ repoRoot, args }: { repoRoot: string; args: string[] }) { + const result = spawnSync('git', args, { + cwd: repoRoot, + encoding: 'buffer', + maxBuffer: 50 * 1024 * 1024, + }) + if (result.status !== 0) + throw new Error(result.stderr.toString('utf8') || `git ${args[0]} failed`) + return result.stdout.toString('utf8').split('\0').filter(Boolean) +} + +export function gitHeadSha({ repoRoot }: { repoRoot: string }) { + const result = spawnSync('git', ['rev-parse', 'HEAD'], { + cwd: repoRoot, + encoding: 'utf8', + }) + if (result.status !== 0) throw new Error(result.stderr || 'git rev-parse failed') + return result.stdout.trim() +} + +export function assertCleanWorktree({ repoRoot }: { repoRoot: string }) { + const result = spawnSync('git', ['status', '--porcelain'], { + cwd: repoRoot, + encoding: 'utf8', + }) + if (result.status !== 0) throw new Error(result.stderr || 'git status failed') + if (result.stdout.trim()) + throw new Error('Refusing to assemble from a dirty worktree. Commit or stash first.') +} + +export function extractHeadArchive({ repoRoot, dest }: { repoRoot: string; dest: string }) { + const archive = spawnSync('git', ['archive', '--format=tar', 'HEAD'], { + cwd: repoRoot, + maxBuffer: 200 * 1024 * 1024, + }) + if (archive.status !== 0) throw new Error(archive.stderr.toString('utf8') || 'git archive failed') + const extract = spawnSync('tar', ['-xf', '-', '-C', dest], { + input: archive.stdout, + maxBuffer: 200 * 1024 * 1024, + }) + if (extract.status !== 0) throw new Error(extract.stderr.toString('utf8') || 'tar extract failed') +} diff --git a/tools/create-basilic/src/pack.test.ts b/tools/create-basilic/src/pack.test.ts new file mode 100644 index 00000000..4c1cb1d6 --- /dev/null +++ b/tools/create-basilic/src/pack.test.ts @@ -0,0 +1,41 @@ +import { spawnSync } from 'node:child_process' +import { existsSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { packageRoot } from './paths.js' + +const runPack = process.env.CREATE_BASILIC_PACK_TEST === '1' + +describe.skipIf(!runPack)('npm pack outside the workspace', () => { + it('packs and generates from the extracted tarball', () => { + const work = mkdtempSync(join(tmpdir(), 'create-basilic-pack-')) + const pack = spawnSync('npm', ['pack', '--pack-destination', work], { + cwd: packageRoot, + encoding: 'utf8', + }) + expect(pack.status, pack.stderr).toBe(0) + const tarball = pack.stdout.trim().split('\n').at(-1) + expect(tarball).toBeTruthy() + const tarballPath = join(work, tarball ?? '') + expect(existsSync(tarballPath)).toBe(true) + + const extracted = join(work, 'extracted') + mkdirSync(extracted) + const untar = spawnSync('tar', ['-xzf', tarballPath, '-C', extracted], { encoding: 'utf8' }) + expect(untar.status, untar.stderr).toBe(0) + const packedRoot = join(extracted, 'package') + expect(existsSync(join(packedRoot, 'dist/cli.js'))).toBe(true) + expect(existsSync(join(packedRoot, 'template/package.json'))).toBe(true) + + const dest = join(work, 'from-tarball') + const generate = spawnSync('node', [join(packedRoot, 'dist/cli.js'), dest, '--yes'], { + encoding: 'utf8', + }) + expect(generate.status, generate.stderr).toBe(0) + expect(existsSync(join(dest, 'package.json'))).toBe(true) + expect(existsSync(join(dest, 'apps/docu'))).toBe(false) + expect(existsSync(join(dest, 'tools/create-basilic'))).toBe(false) + rmSync(work, { recursive: true, force: true }) + }) +}) diff --git a/tools/create-basilic/src/paths.ts b/tools/create-basilic/src/paths.ts new file mode 100644 index 00000000..2925d59b --- /dev/null +++ b/tools/create-basilic/src/paths.ts @@ -0,0 +1,12 @@ +import { dirname, join } from 'node:path' +import { fileURLToPath } from 'node:url' + +const srcDir = dirname(fileURLToPath(import.meta.url)) + +export const packageRoot = join(srcDir, '..') + +export const repoRootFromPackage = join(packageRoot, '../..') + +export const manifestPath = join(packageRoot, 'manifest.json') + +export const bundledTemplateRoot = join(packageRoot, 'template') diff --git a/tools/create-basilic/src/project-name.test.ts b/tools/create-basilic/src/project-name.test.ts new file mode 100644 index 00000000..9c1a4cae --- /dev/null +++ b/tools/create-basilic/src/project-name.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from 'vitest' +import { parseProjectName, ValidationError } from './project-name.js' + +describe('parseProjectName', () => { + it('slugifies directories with spaces', () => { + expect(parseProjectName({ directory: '/tmp/My App' })).toEqual({ + slug: 'my-app', + displayName: 'My App', + packageName: 'my-app', + }) + }) + + it('accepts dotted names', () => { + expect(parseProjectName({ directory: 'acme.app' }).packageName).toBe('acme.app') + }) + + it('rejects empty names', () => { + expect(() => parseProjectName({ directory: ' ' })).toThrow(ValidationError) + }) + + it('rejects names that sanitize to empty', () => { + expect(() => parseProjectName({ directory: '!!!' })).toThrow(ValidationError) + }) + + it('rejects a leading-dot basename', () => { + expect(() => parseProjectName({ directory: '.hidden' })).toThrow(ValidationError) + }) +}) diff --git a/tools/create-basilic/src/project-name.ts b/tools/create-basilic/src/project-name.ts new file mode 100644 index 00000000..9077651d --- /dev/null +++ b/tools/create-basilic/src/project-name.ts @@ -0,0 +1,45 @@ +import { basename } from 'node:path' +import { exitCodes } from './exit-codes.js' + +export type ProjectName = { + slug: string + displayName: string + packageName: string +} + +export class ValidationError extends Error { + exitCode = exitCodes.validation +} + +export function parseProjectName({ directory }: { directory: string }): ProjectName { + const base = basename(directory).trim() + if (!base) throw new ValidationError('Destination directory name is empty') + + const slug = base + .toLowerCase() + .replace(/[\s_]+/g, '-') + .replace(/[^a-z0-9.-]/g, '') + .replace(/-+/g, '-') + .replace(/^-|-$/g, '') + + if (!slug) throw new ValidationError(`Cannot derive an npm-safe name from "${base}"`) + if (slug.length > 214) throw new ValidationError('Project name exceeds 214 characters') + if (!/^[a-z0-9]/.test(slug)) + throw new ValidationError('Project name must start with a letter or number') + if (/--/.test(slug) || slug.endsWith('.') || slug.startsWith('.')) + throw new ValidationError(`Invalid npm package name "${slug}"`) + + const displayName = slug + .split(/[-.]/) + .filter(Boolean) + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' ') + + return { slug, displayName, packageName: slug } +} + +export function assertNode24() { + const major = Number(process.versions.node.split('.')[0]) + if (major !== 24) + throw new ValidationError(`create-basilic requires Node.js 24.x (found ${process.version})`) +} diff --git a/tools/create-basilic/src/transforms/index.test.ts b/tools/create-basilic/src/transforms/index.test.ts new file mode 100644 index 00000000..3e5b4777 --- /dev/null +++ b/tools/create-basilic/src/transforms/index.test.ts @@ -0,0 +1,45 @@ +import { mkdirSync, readFileSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' +import { describe, expect, it } from 'vitest' +import { applyProjectTransforms } from './index.js' + +describe('applyProjectTransforms', () => { + it('rewrites audited display strings and the mobile scheme', () => { + const root = join(tmpdir(), `create-basilic-xform-${Date.now()}`) + mkdirSync(join(root, 'apps/web/app/auth/login'), { recursive: true }) + mkdirSync(join(root, 'apps/mobile/.maestro/flows'), { recursive: true }) + writeFileSync(join(root, 'package.json'), '{"name":"basilic","private":true}\n') + writeFileSync( + join(root, 'apps/web/app/auth/login/login-form.tsx'), + '

Welcome to Basilic

\n', + ) + writeFileSync( + join(root, 'apps/mobile/app.json'), + '{"expo":{"scheme":"com.blockmatic.basilic"}}\n', + ) + writeFileSync( + join(root, 'apps/mobile/.maestro/flows/home.yml'), + 'appId: com.blockmatic.basilic\n', + ) + writeFileSync( + join(root, 'apps/web/next.config.mjs'), + "const apiProjectName = 'basilic-fastify'\nconst teamSlug = 'gaboesquivel'\n", + ) + + applyProjectTransforms({ + destRoot: root, + name: { slug: 'acme', displayName: 'Acme', packageName: 'acme' }, + }) + + expect(JSON.parse(readFileSync(join(root, 'package.json'), 'utf8')).name).toBe('acme') + expect(readFileSync(join(root, 'apps/web/app/auth/login/login-form.tsx'), 'utf8')).toContain( + 'Welcome to Acme', + ) + expect(readFileSync(join(root, 'apps/mobile/app.json'), 'utf8')).toContain('com.example.acme') + expect(readFileSync(join(root, 'apps/web/next.config.mjs'), 'utf8')).toContain("'acme-api'") + expect(readFileSync(join(root, 'apps/web/next.config.mjs'), 'utf8')).not.toContain( + 'gaboesquivel', + ) + }) +}) diff --git a/tools/create-basilic/src/transforms/index.ts b/tools/create-basilic/src/transforms/index.ts new file mode 100644 index 00000000..4622b5f4 --- /dev/null +++ b/tools/create-basilic/src/transforms/index.ts @@ -0,0 +1,169 @@ +import { existsSync, readFileSync, writeFileSync } from 'node:fs' +import { join } from 'node:path' +import type { ProjectName } from '../project-name.js' + +export function applyProjectTransforms({ + destRoot, + name, +}: { + destRoot: string + name: ProjectName +}) { + const replacements = projectReplacements({ name }) + for (const { path, from, to } of replacements) { + const abs = join(destRoot, path) + if (!existsSync(abs)) continue + const source = readFileSync(abs, 'utf8') + if (!source.includes(from)) continue + writeFileSync(abs, source.replaceAll(from, to)) + } + + rewriteRootPackage({ destRoot, name }) + rewriteMobileScheme({ destRoot, name }) + rewriteNextConfig({ destRoot, name }) + rewriteWebEnvUrls({ destRoot }) +} + +function projectReplacements({ name }: { name: ProjectName }) { + return [ + { + path: 'apps/web/app/auth/login/login-form.tsx', + from: 'Welcome to Basilic', + to: `Welcome to ${name.displayName}`, + }, + { + path: 'apps/web/e2e/public.spec.ts', + from: 'Welcome to Basilic', + to: `Welcome to ${name.displayName}`, + }, + { + path: 'apps/web/app/layout.tsx', + from: "default: 'Basilic'", + to: `default: '${name.displayName}'`, + }, + { path: 'apps/web/app/layout.tsx', from: "'%s | Basilic'", to: `'%s | ${name.displayName}'` }, + { + path: 'apps/web/app/layout.tsx', + from: 'Basilic web dashboard', + to: `${name.displayName} web dashboard`, + }, + { + path: 'apps/web/app/(dashboard)/sidebar.tsx', + from: ' Basilic\n', + to: ` ${name.displayName}\n`, + }, + { + path: 'apps/web/app/auth/login/page.tsx', + from: ' Basilic\n', + to: ` ${name.displayName}\n`, + }, + { + path: 'apps/web/app/auth/callback/magiclink/route.ts', + from: 'Enter code - Basilic', + to: `Enter code - ${name.displayName}`, + }, + { path: 'apps/web/app/terms/page.tsx', from: 'using Basilic', to: `using ${name.displayName}` }, + { + path: 'apps/api/src/plugins/openapi.ts', + from: "title: 'Basilic API'", + to: `title: '${name.displayName} API'`, + }, + { + path: 'apps/api/src/plugins/openapi.ts', + from: 'Basilic API documentation', + to: `${name.displayName} API documentation`, + }, + { + path: 'apps/api/scripts/generate-openapi.ts', + from: "title: 'Basilic API'", + to: `title: '${name.displayName} API'`, + }, + { + path: 'apps/api/scripts/generate-openapi.ts', + from: 'Basilic API documentation', + to: `${name.displayName} API documentation`, + }, + { + path: 'apps/api/src/routes/root.ts', + from: 'Basilic Fastify API', + to: `${name.displayName} Fastify API`, + }, + { + path: 'apps/api/src/routes/root.spec.ts', + from: 'Basilic Fastify API', + to: `${name.displayName} Fastify API`, + }, + { + path: 'apps/api/src/routes/reference/template.ts', + from: 'API Reference - Basilic', + to: `API Reference - ${name.displayName}`, + }, + { + path: 'packages/cli/src/cli.ts', + from: 'CLI for Basilic API', + to: `CLI for ${name.displayName} API`, + }, + { + path: 'packages/cli/package.json', + from: 'Basilic Fastify API', + to: `${name.displayName} Fastify API`, + }, + { + path: 'packages/cli/README.md', + from: 'the Basilic Fastify API', + to: `the ${name.displayName} Fastify API`, + }, + { path: '.deepsec/deepsec.config.ts', from: 'id: "app"', to: `id: "${name.slug}"` }, + { + path: '.deepsec/deepsec.config.ts', + from: 'https://github.com/example/app/blob/main', + to: `https://github.com/example/${name.slug}/blob/main`, + }, + ] +} + +function rewriteRootPackage({ destRoot, name }: { destRoot: string; name: ProjectName }) { + const path = join(destRoot, 'package.json') + const pkg = JSON.parse(readFileSync(path, 'utf8')) as { + name: string + basilic?: unknown + } + pkg.name = name.packageName + writeFileSync(path, `${JSON.stringify(pkg, null, 2)}\n`) +} + +function rewriteMobileScheme({ destRoot, name }: { destRoot: string; name: ProjectName }) { + const scheme = `com.example.${name.slug.replace(/\./g, '-')}` + const appJsonPath = join(destRoot, 'apps/mobile/app.json') + if (existsSync(appJsonPath)) { + const source = readFileSync(appJsonPath, 'utf8').replaceAll('com.blockmatic.basilic', scheme) + writeFileSync(appJsonPath, source) + } + const maestroPath = join(destRoot, 'apps/mobile/.maestro/flows/home.yml') + if (existsSync(maestroPath)) { + const source = readFileSync(maestroPath, 'utf8').replaceAll('com.blockmatic.basilic', scheme) + writeFileSync(maestroPath, source) + } +} + +function rewriteNextConfig({ destRoot, name }: { destRoot: string; name: ProjectName }) { + const path = join(destRoot, 'apps/web/next.config.mjs') + if (!existsSync(path)) return + const slug = name.slug.replace(/\./g, '-') + const source = readFileSync(path, 'utf8') + .replace("const apiProjectName = 'basilic-fastify'", `const apiProjectName = '${slug}-api'`) + .replace("const teamSlug = 'gaboesquivel'", "const teamSlug = 'your-team'") + writeFileSync(path, source) +} + +function rewriteWebEnvUrls({ destRoot }: { destRoot: string }) { + for (const file of ['apps/web/.env.production', 'apps/web/.env.staging']) { + const path = join(destRoot, file) + if (!existsSync(path)) continue + const source = readFileSync(path, 'utf8').replaceAll( + 'https://basilic-fastify.vercel.app', + 'https://your-api.vercel.app', + ) + writeFileSync(path, source) + } +} diff --git a/tools/create-basilic/tsconfig.json b/tools/create-basilic/tsconfig.json new file mode 100644 index 00000000..5630a762 --- /dev/null +++ b/tools/create-basilic/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@repo/typescript-config/base.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "resolveJsonModule": true + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "template"] +} diff --git a/tools/create-basilic/tsup.config.ts b/tools/create-basilic/tsup.config.ts new file mode 100644 index 00000000..27e8fd65 --- /dev/null +++ b/tools/create-basilic/tsup.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsup' + +export default defineConfig({ + entry: ['src/cli.ts'], + format: ['esm'], + target: 'node24', + sourcemap: true, + clean: true, + outDir: 'dist', + noExternal: ['commander'], + banner: { + js: '#!/usr/bin/env node', + }, +}) diff --git a/tools/create-basilic/vitest.config.ts b/tools/create-basilic/vitest.config.ts new file mode 100644 index 00000000..3db174c6 --- /dev/null +++ b/tools/create-basilic/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + include: ['src/**/*.test.ts'], + globals: true, + environment: 'node', + testTimeout: 30_000, + }, +}) diff --git a/turbo.json b/turbo.json index 6b23a52d..94527e42 100644 --- a/turbo.json +++ b/turbo.json @@ -1,7 +1,19 @@ { "$schema": "https://turbo.build/schema.json", "ui": "tui", - "globalPassThroughEnv": ["QA_SKIP_TESTS", "SKIP_KILL_PORTS", "SKIP_BUILD"], + "globalPassThroughEnv": [ + "QA_SKIP_TESTS", + "SKIP_KILL_PORTS", + "SKIP_BUILD", + "PR_TITLE", + "PR_BODY", + "PR_BASE_SHA", + "PR_HEAD_SHA", + "CREATE_BASILIC_LOCKFILE", + "CREATE_BASILIC_ALLOW_DIRTY", + "CREATE_BASILIC_TEMPLATE_DIR", + "CREATE_BASILIC_PACK_TEST" + ], "tasks": { "generate": { "dependsOn": [],