Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changeset/huge-phones-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
"dev-workflows": minor
---

### `devw add` — interactive registry flow

- `devw add` without arguments opens an interactive flow: browse categories, multi-select rules, confirm, and install in one session
- `devw add <category>/<rule>` installs a rule directly from the GitHub registry
- `devw add --list` shows all available rules from the registry
- Old block format (`devw add typescript-strict`) is detected and shows a migration error

### `devw remove` — pulled rules

- `devw remove <category>/<rule>` removes a pulled rule and updates config
- `devw remove` without arguments opens an interactive multi-select of installed rules

### Removed

- `devw pull` command (absorbed into `devw add`)
- Local blocks system (`blocks/registry`, `blocks/installer`)
17 changes: 10 additions & 7 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,26 @@ CLI tool that compiles developer rules into editor-specific config files (CLAUDE

- Monorepo with pnpm workspaces
- CLI in `packages/cli/` (TypeScript, commander)
- Rule blocks in `content/blocks/` (YAML)
- Rules in `content/rules/` (Markdown with YAML frontmatter, hosted on GitHub)
- Tests with node:test

## Architecture

- `content/core/` → tool-agnostic source of truth (workflows, skills, templates)
- `content/blocks/` → precooked rule blocks (YAML)
- `content/rules/` → official rule files (Markdown), fetched from GitHub via `devw add`
- `packages/cli/src/bridges/` → per-tool adapters that translate core → native format
- Bridges only translate. They do not add new intent or logic.

## Key commands

```bash
pnpm install # install deps
pnpm build # build CLI
pnpm test # run tests
pnpm dev # dev mode
pnpm install # install deps
pnpm build # build CLI
pnpm test # run tests
pnpm dev # dev mode
devw add # interactive: browse and install rules from registry
devw add typescript/strict # direct: install specific rule
devw add --list # list available rules
```

## Specs (read before implementing)
Expand All @@ -32,7 +35,7 @@ pnpm dev # dev mode
- `docs/internal/CLI_SPEC_v0.2.1.md` → v0.2.1 UX polish specification (COMPLETE)
- `docs/internal/DOCS_SPEC.md` → Mintlify documentation spec (COMPLETE)
- `docs/internal/WATCH_SPEC.md` → v0.3 watch mode specification (COMPLETE)
- `docs/internal/PULL_SPEC.md` → v0.4 Pull rules specification (IMPLEMENT THIS)
- `docs/internal/PULL_SPEC.md` → v0.4 Pull rules specification (ABSORBED into `devw add`)
- `docs/internal/DECISIONS.md` → accepted decisions (source of truth if conflict)
- `docs/internal/` is gitignored — internal specs not published

Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Examples:

> This repo is still in early stage.

Expected setup (will be finalized in Block 4 - CLI):
Expected setup:

```bash
pnpm install
Expand Down
74 changes: 51 additions & 23 deletions apps/landing/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -1754,78 +1754,106 @@ <h1>
</div>
</section>

<!-- ═══════ BLOCKS ═══════ -->
<!-- ═══════ REGISTRY ═══════ -->
<section class="blocks-section reveal" id="blocks">
<div class="blocks-header">
<div class="section-label">Prebuilt rule blocks</div>
<div class="section-label">Rules registry</div>
<div class="section-title">Don't write rules from scratch.</div>
<p class="section-sub">
Install curated rule packs with one command. Stack them — TypeScript + React + Tailwind in seconds.
Browse, select, and install rules interactively. No need to memorize names.
</p>
</div>

<div class="registry-demo">
<div class="registry-panel registry-panel--main">
<div class="registry-bar">
<span class="registry-label">Interactive mode</span>
</div>
<div class="registry-body">
<div class="reg-line"><span class="term-prompt">$</span> <span class="term-cmd">devw add</span></div>
<div class="reg-line"><span class="term-success">&#10004;</span> Choose a category: <span class="term-val">typescript</span></div>
<div class="reg-line"><span class="term-success">&#10004;</span> Select rules to add: <span class="term-val">strict</span> <span class="term-muted">&mdash; Strict TypeScript conventions</span></div>
<div class="reg-line"><span class="term-success">&#10004;</span> Install 1 rule? <span class="term-val">Yes</span></div>
<div class="reg-line">&nbsp;</div>
<div class="reg-line"> <span class="term-success">&#10003;</span> Added <span class="term-val">typescript/strict</span> <span class="term-muted">(8 rules)</span></div>
<div class="reg-line"> <span class="term-success">&#10003;</span> Compiled for <span class="term-file">claude</span>, <span class="term-file">cursor</span></div>
</div>
</div>
<div class="registry-panel registry-panel--alt">
<div class="registry-bar">
<span class="registry-label">Direct mode</span>
</div>
<div class="registry-body">
<div class="reg-line"><span class="term-prompt">$</span> <span class="term-cmd">devw add</span> <span class="term-val">typescript/strict</span></div>
<div class="reg-line">&nbsp;</div>
<div class="reg-line"> <span class="term-success">&#10003;</span> Added <span class="term-val">typescript/strict</span> <span class="term-muted">(8 rules)</span></div>
<div class="reg-line"> <span class="term-success">&#10003;</span> Compiled for <span class="term-file">claude</span>, <span class="term-file">cursor</span></div>
</div>
</div>
</div>

<div class="blocks-grid">
<div class="block-card">
<div class="block-name">typescript-strict</div>
<div class="block-name">typescript/strict</div>
<div class="block-desc">No any, explicit returns, union types over enums, no non-null assertions.</div>
<div class="block-install">
<span><span class="cmd">devw add</span> typescript-strict</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add typescript-strict')">
<span><span class="cmd">devw add</span> typescript/strict</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add typescript/strict')">
<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
<svg aria-hidden="true" class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</button>
</div>
</div>
<div class="block-card">
<div class="block-name">react-conventions</div>
<div class="block-name">javascript/react</div>
<div class="block-desc">Hooks rules, component patterns, naming conventions, prop types.</div>
<div class="block-install">
<span><span class="cmd">devw add</span> react-conventions</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add react-conventions')">
<span><span class="cmd">devw add</span> javascript/react</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add javascript/react')">
<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
<svg aria-hidden="true" class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</button>
</div>
</div>
<div class="block-card">
<div class="block-name">nextjs-approuter</div>
<div class="block-name">javascript/nextjs</div>
<div class="block-desc">App Router patterns, RSC boundaries, server actions, metadata API.</div>
<div class="block-install">
<span><span class="cmd">devw add</span> nextjs-approuter</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add nextjs-approuter')">
<span><span class="cmd">devw add</span> javascript/nextjs</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add javascript/nextjs')">
<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
<svg aria-hidden="true" class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</button>
</div>
</div>
<div class="block-card">
<div class="block-name">tailwind</div>
<div class="block-name">css/tailwind</div>
<div class="block-desc">Utility-first, no custom CSS, design tokens, responsive patterns.</div>
<div class="block-install">
<span><span class="cmd">devw add</span> tailwind</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add tailwind')">
<span><span class="cmd">devw add</span> css/tailwind</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add css/tailwind')">
<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
<svg aria-hidden="true" class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</button>
</div>
</div>
<div class="block-card">
<div class="block-name">supabase-rls</div>
<div class="block-name">security/supabase-rls</div>
<div class="block-desc">RLS enforcement on every table, auth patterns, security policies.</div>
<div class="block-install">
<span><span class="cmd">devw add</span> supabase-rls</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add supabase-rls')">
<span><span class="cmd">devw add</span> security/supabase-rls</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add security/supabase-rls')">
<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
<svg aria-hidden="true" class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</button>
</div>
</div>
<div class="block-card">
<div class="block-name">testing-basics</div>
<div class="block-name">testing/vitest</div>
<div class="block-desc">Test naming, coverage expectations, mock patterns, arrange-act-assert.</div>
<div class="block-install">
<span><span class="cmd">devw add</span> testing-basics</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add testing-basics')">
<span><span class="cmd">devw add</span> testing/vitest</span>
<button class="block-copy" aria-label="Copy install command" onclick="copyBlock(this, 'devw add testing/vitest')">
<svg aria-hidden="true" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 01-2-2V4a2 2 0 012-2h9a2 2 0 012 2v1"/></svg>
<svg aria-hidden="true" class="check-icon" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
</button>
Expand All @@ -1838,12 +1866,12 @@ <h1>
<section class="proof-strip reveal">
<div class="proof-items">
<div class="proof-item">
<div class="proof-num">137</div>
<div class="proof-num">243</div>
<div class="proof-label">Tests passing</div>
</div>
<div class="proof-item">
<div class="proof-num">6</div>
<div class="proof-label">Rule blocks</div>
<div class="proof-label">Registry rules</div>
</div>
<div class="proof-item">
<div class="proof-num">5</div>
Expand Down
2 changes: 1 addition & 1 deletion apps/landing/scripts/copy.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function copyInstall(el) {

function copyBlock(btn, text) {
navigator.clipboard.writeText(text);
trackEvent('copy_command', { command: text, location: 'blocks' });
trackEvent('copy_command', { command: text, location: 'registry' });
btn.classList.add('copied');
setTimeout(() => btn.classList.remove('copied'), 1500);
}
Expand Down
36 changes: 14 additions & 22 deletions apps/landing/scripts/terminal.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,20 @@ const TERM_TABS = {
{ html: '<span class="term-success"> \u2713</span> Created <span class="term-file">.dwf/config.yml</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> Created <span class="term-file">.dwf/rules/</span> <span class="term-muted">(5 scope files)</span>', delay: 250 },
{ html: '', delay: 300 },
{ html: '<span class="term-success"> Ready.</span> <span class="term-muted">Add rules or install a block to get started.</span>', delay: 400 },
{ html: '<span class="term-success"> Ready.</span> <span class="term-muted">Run</span> devw add <span class="term-muted">to browse the registry.</span>', delay: 400 },
]
},
add: {
comment: '# Install a prebuilt rule block',
comment: '# Add rules from the registry',
lines: [
{ html: '<span class="term-prompt">$</span> <span class="term-cmd">devw add typescript-strict</span>', delay: 0 },
{ html: '<span class="term-prompt">$</span> <span class="term-cmd">devw add</span>', delay: 0 },
{ html: '', delay: 400 },
{ html: '<span class="term-success"> \u2713</span> Added <span class="term-val">ts-strict-no-any</span> to <span class="term-file">conventions.yml</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> Added <span class="term-val">ts-strict-explicit-returns</span> to <span class="term-file">conventions.yml</span>', delay: 250 },
{ html: '<span class="term-success"> \u2713</span> Added <span class="term-val">ts-strict-no-enums</span> to <span class="term-file">conventions.yml</span>', delay: 250 },
{ html: '<span class="term-success"> \u2713</span> Added <span class="term-val">ts-strict-no-non-null</span> to <span class="term-file">conventions.yml</span>', delay: 250 },
{ html: '<span class="term-success"> \u2714</span> Choose a category: <span class="term-val">typescript</span>', delay: 500 },
{ html: '<span class="term-success"> \u2714</span> Select rules to add: <span class="term-val">strict</span> <span class="term-muted">\u2014 Strict TypeScript conventions</span>', delay: 500 },
{ html: '<span class="term-success"> \u2714</span> Install 1 rule? <span class="term-val">Yes</span>', delay: 400 },
{ html: '', delay: 300 },
{ html: '<span class="term-muted"> Block registered in config.yml</span>', delay: 200 },
{ html: '<span class="term-success"> 4 rules added.</span> <span class="term-muted">Run</span> devw compile <span class="term-muted">to apply.</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> Added <span class="term-val">typescript/strict</span> <span class="term-muted">(8 rules)</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> Compiled for <span class="term-file">claude</span>, <span class="term-file">cursor</span>', delay: 250 },
]
},
compile: {
Expand Down Expand Up @@ -66,7 +65,7 @@ const TERM_TABS = {
{ html: '', delay: 200 },
{ html: '<span class="term-success"> \u2713</span> config.yml <strong>valid</strong>', delay: 300 },
{ html: '<span class="term-success"> \u2713</span> rules.yml <strong>valid</strong>', delay: 250 },
{ html: '<span class="term-success"> \u2713</span> 2 blocks installed <span class="term-success">ok</span>', delay: 250 },
{ html: '<span class="term-success"> \u2713</span> 2 pulled rules <span class="term-success">ok</span>', delay: 250 },
{ html: '<span class="term-warn"> \u26a0</span> CLAUDE.md <span class="term-warn"><strong>stale</strong> (recompile needed)</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> .cursor/rules <span class="term-success">up to date</span>', delay: 250 },
{ html: '<span class="term-success"> \u2713</span> GEMINI.md <span class="term-success">up to date</span>', delay: 250 },
Expand All @@ -75,7 +74,7 @@ const TERM_TABS = {
]
},
list: {
comment: '# List rules, blocks, or tools',
comment: '# List rules or tools',
lines: [
{ html: '<span class="term-prompt">$</span> <span class="term-cmd">devw list rules</span>', delay: 0 },
{ html: '', delay: 400 },
Expand All @@ -92,19 +91,12 @@ const TERM_TABS = {
]
},
remove: {
comment: '# Remove a rule block',
comment: '# Remove a pulled rule',
lines: [
{ html: '<span class="term-prompt">$</span> <span class="term-cmd">devw remove typescript-strict</span>', delay: 0 },
{ html: '<span class="term-prompt">$</span> <span class="term-cmd">devw remove typescript/strict</span>', delay: 0 },
{ html: '', delay: 400 },
{ html: '<span class="term-muted"> Removing block typescript-strict...</span>', delay: 500 },
{ html: '', delay: 200 },
{ html: '<span class="term-success"> \u2713</span> Removed <span class="term-val">ts-strict-no-any</span> from <span class="term-file">conventions.yml</span>', delay: 250 },
{ html: '<span class="term-success"> \u2713</span> Removed <span class="term-val">ts-strict-explicit-returns</span> from <span class="term-file">conventions.yml</span>', delay: 200 },
{ html: '<span class="term-success"> \u2713</span> Removed <span class="term-val">ts-strict-no-enums</span> from <span class="term-file">conventions.yml</span>', delay: 200 },
{ html: '<span class="term-success"> \u2713</span> Removed <span class="term-val">ts-strict-no-non-null</span> from <span class="term-file">conventions.yml</span>', delay: 200 },
{ html: '', delay: 300 },
{ html: '<span class="term-muted"> Block unregistered from config.yml</span>', delay: 200 },
{ html: '<span class="term-success"> Done.</span> <span class="term-muted">4 rules removed. Run</span> devw compile <span class="term-muted">to update outputs.</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> Removed <span class="term-val">typescript/strict</span>', delay: 350 },
{ html: '<span class="term-success"> \u2713</span> Compiled for <span class="term-file">claude</span>, <span class="term-file">cursor</span>', delay: 250 },
]
}
};
Expand Down
Loading