Skip to content

Commit 93d862a

Browse files
committed
feat(relay): add agent-driven initialization to skill and dormant mode
Enable coding agents to initialize Domscribe without the interactive CLI. Users who install the plugin via marketplace can now have their agent detect the framework, install packages, and edit bundler configs directly. - Add setup/init trigger phrases and Bash/Glob to SKILL.md allowed-tools - Add Setup/Initialization section with framework detection, package mapping, PM detection tables, monorepo handling, and 7-step procedure - Create references/config-patterns.md with detailed config editing instructions for all 8 framework combinations (4 integration patterns) - Add nextSteps field to dormant status tool with actionable agent guidance - Replace "run npx domscribe init" guidance with structured setup steps
1 parent 8007205 commit 93d862a

File tree

4 files changed

+306
-6
lines changed

4 files changed

+306
-6
lines changed

packages/domscribe-relay/src/mcp/tools/dormant-status.tool.spec.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ describe('DormantStatusTool', () => {
1818
expect(structured['cwd']).toBe(cwd);
1919
});
2020

21-
it('should return guidance with setup instructions', async () => {
21+
it('should return guidance explaining dormant state', async () => {
2222
// Arrange
2323
const tool = new DormantStatusTool('/tmp/test');
2424

@@ -28,10 +28,23 @@ describe('DormantStatusTool', () => {
2828
// Assert
2929
const structured = result.structuredContent as Record<string, unknown>;
3030
expect(structured['guidance']).toEqual(expect.any(String));
31-
expect(structured['guidance']).toContain('npx domscribe init');
3231
expect(structured['guidance']).toContain('.domscribe');
3332
});
3433

34+
it('should return actionable next steps for the agent', async () => {
35+
// Arrange
36+
const tool = new DormantStatusTool('/tmp/test');
37+
38+
// Act
39+
const result: CallToolResult = await tool.toolCallback({});
40+
41+
// Assert
42+
const structured = result.structuredContent as Record<string, unknown>;
43+
expect(structured['nextSteps']).toEqual(expect.any(String));
44+
expect(structured['nextSteps']).toContain('package.json');
45+
expect(structured['nextSteps']).toContain('@domscribe');
46+
});
47+
3548
it('should return text content matching structured content', async () => {
3649
// Arrange
3750
const tool = new DormantStatusTool('/tmp/test');

packages/domscribe-relay/src/mcp/tools/dormant-status.tool.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ const DormantStatusToolOutputSchema = McpToolOutputSchema.extend({
2222
cwd: z
2323
.string()
2424
.describe('The working directory where the MCP server was started'),
25-
guidance: z.string().describe('Instructions for setting up Domscribe'),
25+
guidance: z.string().describe('Explanation of why Domscribe is not active'),
26+
nextSteps: z
27+
.string()
28+
.describe('Actionable steps for the agent to set up Domscribe'),
2629
});
2730

2831
type DormantStatusToolOutput = z.infer<typeof DormantStatusToolOutputSchema>;
@@ -48,7 +51,16 @@ export class DormantStatusTool implements McpToolDefinition<
4851
guidance:
4952
'Domscribe is not active in this workspace. ' +
5053
'No .domscribe/ directory was found at or above the current working directory. ' +
51-
'To set up Domscribe, run `npx domscribe init` in your project root.',
54+
'The .domscribe/ directory is created automatically when the dev server starts ' +
55+
'with the Domscribe bundler plugin configured.',
56+
nextSteps:
57+
'To set up Domscribe: ' +
58+
'(1) Read package.json to detect the framework (next/nuxt/react/vue + vite/webpack), ' +
59+
'(2) detect the package manager from lockfiles (pnpm-lock.yaml/yarn.lock/bun.lock), ' +
60+
'(3) install the appropriate @domscribe/* package as a dev dependency, ' +
61+
'(4) edit the bundler config file to add the Domscribe plugin, ' +
62+
'(5) add .domscribe to .gitignore, ' +
63+
'(6) ask the user to start their dev server.',
5264
};
5365

5466
return {

packages/domscribe-relay/src/skills/domscribe/SKILL.md

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
name: domscribe
3-
description: Work with Domscribe — the pixel-to-code bridge. Use when editing or modifying UI components (React, Vue, Next.js, Nuxt), implementing features from captured UI annotations, querying runtime context for source locations, exploring component structure, or when user mentions annotations, queued tasks, UI changes, props, state, DOM, or asks about how elements render at runtime.
4-
allowed-tools: Read, Edit, Write, mcp__domscribe__*, domscribe.*, mcp.domscribe.*, process_next, check_status, explore_component, find_annotations
3+
description: Work with Domscribe — the pixel-to-code bridge. Use when setting up, initializing, or configuring Domscribe for a project, OR when editing or modifying UI components (React, Vue, Next.js, Nuxt), implementing features from captured UI annotations, querying runtime context for source locations, exploring component structure, or when user mentions annotations, queued tasks, UI changes, props, state, DOM, or asks about how elements render at runtime.
4+
allowed-tools: Read, Edit, Write, Bash, Glob, mcp__domscribe__*, domscribe.*, mcp.domscribe.*, process_next, check_status, explore_component, find_annotations
55
---
66

77
# Domscribe
@@ -11,6 +11,71 @@ Domscribe bridges running UI and source code. It maps every rendered element to
1111
- **UI → Code**: User clicks an element in the browser, Domscribe captures it as an annotation with source location, runtime context, and user intent. You claim and implement it.
1212
- **Code → UI**: You're editing a source file and want to know what an element looks like at runtime. Query by file and line to get live props, state, and DOM snapshot.
1313

14+
## Setup / Initialization
15+
16+
If `domscribe.status` returns `active: false`, Domscribe is not yet configured in this workspace. The `.domscribe/` directory is created automatically when the dev server starts with the Domscribe bundler plugin configured. Follow this procedure to set it up:
17+
18+
1. **Confirm dormant state** — call `domscribe.status`. If `active: false`, proceed. Note the `cwd` in the response.
19+
2. **Detect framework** — read `package.json` (at `cwd`, or at the `appRoot` from `domscribe.config.json` if it exists) and match dependencies against the table below.
20+
3. **Detect package manager** — check which lockfile exists at the project root.
21+
4. **Install the package** — run the appropriate install command via Bash (e.g., `pnpm add -D @domscribe/next`).
22+
5. **Edit the bundler config** — read the config file and apply the integration pattern. Load `references/config-patterns.md` for the exact import, transformation, and example for each framework.
23+
6. **Update `.gitignore`** — if `.domscribe` is not already listed, append a `# Domscribe artifacts` comment and `.domscribe` entry.
24+
7. **Inform the user** — tell them to start (or restart) their dev server. Domscribe activates automatically on first run.
25+
26+
### Framework Detection
27+
28+
Check `dependencies` and `devDependencies` in `package.json`. Match top-down (first match wins):
29+
30+
| Dependency | Framework |
31+
| ------------------- | ------------- |
32+
| `next` | next |
33+
| `nuxt` | nuxt |
34+
| `react` + `vite` | react-vite |
35+
| `react` (no `vite`) | react-webpack |
36+
| `vue` + `vite` | vue-vite |
37+
| `vue` (no `vite`) | vue-webpack |
38+
| `vite` only | other-vite |
39+
| `webpack` only | other-webpack |
40+
41+
### Package Mapping
42+
43+
| Framework | Package | Config file |
44+
| ------------- | ---------------------- | ------------------- |
45+
| next | `@domscribe/next` | `next.config.ts` |
46+
| nuxt | `@domscribe/nuxt` | `nuxt.config.ts` |
47+
| react-vite | `@domscribe/react` | `vite.config.ts` |
48+
| react-webpack | `@domscribe/react` | `webpack.config.js` |
49+
| vue-vite | `@domscribe/vue` | `vite.config.ts` |
50+
| vue-webpack | `@domscribe/vue` | `webpack.config.js` |
51+
| other-vite | `@domscribe/transform` | `vite.config.ts` |
52+
| other-webpack | `@domscribe/transform` | `webpack.config.js` |
53+
54+
### Package Manager Detection
55+
56+
| Lockfile | Package manager |
57+
| ------------------------ | --------------- |
58+
| `pnpm-lock.yaml` | pnpm |
59+
| `yarn.lock` | yarn |
60+
| `bun.lock` / `bun.lockb` | bun |
61+
| (none) | npm |
62+
63+
Install command pattern: `<pm> add -D <package>` (pnpm/yarn/bun) or `npm install -D <package>`.
64+
65+
### Monorepo Projects
66+
67+
If `domscribe.config.json` exists at the project root with an `appRoot` field, the frontend app lives in that subdirectory. Install packages and find the bundler config relative to `appRoot`.
68+
69+
If the project appears to be a monorepo (e.g., `apps/`, `packages/` directories, workspace config in `package.json`) but no `domscribe.config.json` exists, ask the user which directory contains the frontend app. Then write `domscribe.config.json` at the project root:
70+
71+
```json
72+
{ "appRoot": "apps/web" }
73+
```
74+
75+
### After Setup
76+
77+
The MCP server starts in dormant mode when no `.domscribe/` directory exists. After the user starts their dev server, the bundler plugin creates `.domscribe/` automatically. The MCP server will need to restart to detect the new workspace and transition to active mode with the full tool suite.
78+
1479
## Editing Components (Code → UI)
1580

1681
**Why query runtime state?** Source code alone doesn't tell you what props a component actually received, whether a conditional branch rendered, what CSS classes were applied, or what text the user sees. `domscribe.query.bySource` gives you the live truth from the browser.
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# Domscribe Config Patterns
2+
3+
How to edit each framework's bundler config to integrate Domscribe. The 8 framework combinations reduce to 4 distinct integration patterns.
4+
5+
---
6+
7+
## Next.js — HOF Wrapper
8+
9+
**Import to add:**
10+
11+
```typescript
12+
import { withDomscribe } from '@domscribe/next';
13+
```
14+
15+
**How to edit:**
16+
17+
Wrap the existing default export with `withDomscribe()()`. This is a higher-order function that returns a config transformer.
18+
19+
- If the file has `export default <expression>` — change to `export default withDomscribe()(<expression>)`
20+
- If the file has `const config = ...; export default config` — change to `export default withDomscribe()(config)`
21+
- If the file already uses another wrapper (e.g., `withMDX`), compose them: `export default withDomscribe()(withMDX(config))`
22+
23+
**Full example (`next.config.ts`):**
24+
25+
```typescript
26+
import type { NextConfig } from 'next';
27+
import { withDomscribe } from '@domscribe/next';
28+
29+
const nextConfig: NextConfig = {};
30+
31+
export default withDomscribe()(nextConfig);
32+
```
33+
34+
---
35+
36+
## Nuxt — Module Registration
37+
38+
**How to edit:**
39+
40+
Add `'@domscribe/nuxt'` as a string to the `modules` array inside `defineNuxtConfig()`. No import needed — Nuxt resolves the module by package name.
41+
42+
- If no `modules` key exists in the config object, add it: `modules: ['@domscribe/nuxt']`
43+
- If `modules` exists, append `'@domscribe/nuxt'` to the array
44+
45+
**Full example (`nuxt.config.ts`):**
46+
47+
```typescript
48+
export default defineNuxtConfig({
49+
modules: ['@domscribe/nuxt'],
50+
});
51+
```
52+
53+
---
54+
55+
## Vite Plugin (React / Vue / Other)
56+
57+
Three variants — same pattern, different import path:
58+
59+
| Framework | Import path |
60+
| ---------- | ----------------------------------- |
61+
| react-vite | `@domscribe/react/vite` |
62+
| vue-vite | `@domscribe/vue/vite` |
63+
| other-vite | `@domscribe/transform/plugins/vite` |
64+
65+
**Import to add** (use the path from the table above):
66+
67+
```typescript
68+
import { domscribe } from '@domscribe/react/vite';
69+
```
70+
71+
**How to edit:**
72+
73+
Add `domscribe()` to the `plugins` array inside `defineConfig()`. Place it after the framework plugin (e.g., after `react()` or `vue()`).
74+
75+
- If `plugins` exists, append `domscribe()` to the array
76+
- If `plugins` doesn't exist, add `plugins: [domscribe()]`
77+
78+
**Full example — React + Vite (`vite.config.ts`):**
79+
80+
```typescript
81+
import { defineConfig } from 'vite';
82+
import react from '@vitejs/plugin-react';
83+
import { domscribe } from '@domscribe/react/vite';
84+
85+
export default defineConfig({
86+
plugins: [react(), domscribe()],
87+
});
88+
```
89+
90+
**Full example — Vue + Vite (`vite.config.ts`):**
91+
92+
```typescript
93+
import { defineConfig } from 'vite';
94+
import vue from '@vitejs/plugin-vue';
95+
import { domscribe } from '@domscribe/vue/vite';
96+
97+
export default defineConfig({
98+
plugins: [vue(), domscribe()],
99+
});
100+
```
101+
102+
**Full example — Other + Vite (`vite.config.ts`):**
103+
104+
```typescript
105+
import { defineConfig } from 'vite';
106+
import { domscribe } from '@domscribe/transform/plugins/vite';
107+
108+
export default defineConfig({
109+
plugins: [domscribe()],
110+
});
111+
```
112+
113+
---
114+
115+
## Webpack Plugin + Loader (React / Vue / Other)
116+
117+
Three variants — same pattern, different import path:
118+
119+
| Framework | Import path |
120+
| ------------- | -------------------------------------- |
121+
| react-webpack | `@domscribe/react/webpack` |
122+
| vue-webpack | `@domscribe/vue/webpack` |
123+
| other-webpack | `@domscribe/transform/plugins/webpack` |
124+
125+
**Two edits required:**
126+
127+
### Edit 1 — Add the transform loader rule
128+
129+
Add a pre-enforce loader rule to `module.rules`. This must run before other loaders:
130+
131+
```javascript
132+
{
133+
test: /\.[jt]sx?$/,
134+
exclude: /node_modules/,
135+
enforce: 'pre',
136+
use: [
137+
{
138+
loader: '@domscribe/transform/webpack-loader',
139+
options: { enabled: process.env.NODE_ENV !== 'production' },
140+
},
141+
],
142+
}
143+
```
144+
145+
### Edit 2 — Add the webpack plugin
146+
147+
Add the plugin instance to the `plugins` array. Use the import path from the table above:
148+
149+
```javascript
150+
const { DomscribeWebpackPlugin } = require('@domscribe/react/webpack');
151+
152+
// In the plugins array:
153+
new DomscribeWebpackPlugin({
154+
enabled: process.env.NODE_ENV !== 'production',
155+
overlay: true,
156+
});
157+
```
158+
159+
**Full example — React + Webpack (`webpack.config.js`):**
160+
161+
```javascript
162+
const { DomscribeWebpackPlugin } = require('@domscribe/react/webpack');
163+
164+
const isDevelopment = process.env.NODE_ENV !== 'production';
165+
166+
module.exports = {
167+
module: {
168+
rules: [
169+
{
170+
test: /\.[jt]sx?$/,
171+
exclude: /node_modules/,
172+
enforce: 'pre',
173+
use: [
174+
{
175+
loader: '@domscribe/transform/webpack-loader',
176+
options: { enabled: isDevelopment },
177+
},
178+
],
179+
},
180+
// ... existing loaders
181+
],
182+
},
183+
plugins: [
184+
new DomscribeWebpackPlugin({
185+
enabled: isDevelopment,
186+
overlay: true,
187+
}),
188+
],
189+
};
190+
```
191+
192+
---
193+
194+
## Package Manager Install Commands
195+
196+
| Lockfile | Package manager | Install command |
197+
| ------------------------ | --------------- | ---------------- |
198+
| `pnpm-lock.yaml` | pnpm | `pnpm add -D` |
199+
| `yarn.lock` | yarn | `yarn add -D` |
200+
| `bun.lock` / `bun.lockb` | bun | `bun add -D` |
201+
| (none) | npm | `npm install -D` |
202+
203+
## Gitignore
204+
205+
If `.domscribe` is not already listed in `.gitignore`, append:
206+
207+
```
208+
# Domscribe artifacts
209+
.domscribe
210+
```

0 commit comments

Comments
 (0)