@donotdev/cli 0.0.20 → 0.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -0
- package/dependencies-matrix.json +86 -19
- package/dist/bin/commands/agent-setup.js +2 -2
- package/dist/bin/commands/build.js +6 -6
- package/dist/bin/commands/bump.js +491 -69
- package/dist/bin/commands/cacheout.js +6 -6
- package/dist/bin/commands/coach.js +6 -6
- package/dist/bin/commands/create-app.js +23 -15
- package/dist/bin/commands/create-project.js +101 -16
- package/dist/bin/commands/db.js +142136 -0
- package/dist/bin/commands/deploy.js +336 -126
- package/dist/bin/commands/dev.js +6 -6
- package/dist/bin/commands/doctor.js +140 -33
- package/dist/bin/commands/emu.js +6 -6
- package/dist/bin/commands/format.js +6 -6
- package/dist/bin/commands/get-demo.js +11 -6
- package/dist/bin/commands/make-admin.js +14210 -13770
- package/dist/bin/commands/preview.js +6 -6
- package/dist/bin/commands/seed.js +142426 -0
- package/dist/bin/commands/setup-cicd.js +8904 -0
- package/dist/bin/commands/setup.js +256 -212
- package/dist/bin/commands/staging.js +343 -127
- package/dist/bin/commands/sync-secrets.js +55 -33
- package/dist/bin/commands/type-check.js +6 -6
- package/dist/bin/commands/wai.js +6 -6
- package/dist/bin/dndev.js +76 -11
- package/dist/bin/donotdev.js +21 -12
- package/dist/index.js +437 -142
- package/package.json +1 -1
- package/templates/app-demo/.env.example +1 -0
- package/templates/{root-consumer → app-demo}/entities/ExampleEntity.ts.example +15 -9
- package/templates/app-demo/index.html.example +1 -1
- package/templates/app-dndev/index.html.example +164 -0
- package/templates/app-dndev/public/logo.svg.example +1 -0
- package/templates/app-dndev/public/manifest.json.example +10 -0
- package/templates/app-dndev/src/App.tsx.example +35 -0
- package/templates/app-dndev/src/components/CockpitLayout.css.example +181 -0
- package/templates/app-dndev/src/components/CockpitLayout.tsx.example +209 -0
- package/templates/app-dndev/src/components/Kanban.css.example +385 -0
- package/templates/app-dndev/src/components/ModeToggle.tsx.example +32 -0
- package/templates/app-dndev/src/components/OverlaySlot.tsx.example +68 -0
- package/templates/app-dndev/src/components/TerminalPanel.css.example +228 -0
- package/templates/app-dndev/src/components/TerminalPanel.tsx.example +714 -0
- package/templates/app-dndev/src/components/markdown-prose.css.example +49 -0
- package/templates/app-dndev/src/components/phases/CaptainLog.tsx.example +107 -0
- package/templates/app-dndev/src/components/phases/ContextTabs.tsx.example +352 -0
- package/templates/app-dndev/src/components/phases/PhaseCard.tsx.example +126 -0
- package/templates/app-dndev/src/components/phases/PhaseDetail.tsx.example +147 -0
- package/templates/app-dndev/src/components/phases/ReviewPanel.tsx.example +115 -0
- package/templates/app-dndev/src/components/phases/phaseData.ts.example +366 -0
- package/templates/app-dndev/src/config/app.ts.example +103 -0
- package/templates/app-dndev/src/config/commands.ts.example +171 -0
- package/templates/app-dndev/src/config/legal.ts.example +170 -0
- package/templates/app-dndev/src/config/providers.ts.example +7 -0
- package/templates/app-dndev/src/globals.css.example +10 -0
- package/templates/app-dndev/src/hooks/useDndevFile.ts.example +144 -0
- package/templates/app-dndev/src/main.tsx.example +21 -0
- package/templates/app-dndev/src/pages/BoardPage.tsx.example +640 -0
- package/templates/app-dndev/src/pages/GrillPage.tsx.example +658 -0
- package/templates/app-dndev/src/pages/HomePage.tsx.example +347 -0
- package/templates/app-dndev/src/pages/NotFoundPage.tsx.example +33 -0
- package/templates/app-dndev/src/pages/PhasesPage.tsx.example +137 -0
- package/templates/app-dndev/src/pages/SettingsPage.tsx.example +64 -0
- package/templates/app-dndev/src/pages/legal/LegalNoticePage.tsx.example +75 -0
- package/templates/app-dndev/src/pages/legal/PrivacyPage.tsx.example +69 -0
- package/templates/app-dndev/src/pages/legal/TermsPage.tsx.example +71 -0
- package/templates/app-dndev/src/stores/dndevStore.ts.example +386 -0
- package/templates/app-dndev/src/themes.css.example +161 -0
- package/templates/app-dndev/terminal-sidecar.cjs.example +341 -0
- package/templates/app-dndev/tsconfig.json.example +9 -0
- package/templates/app-dndev/vite.config.ts.example +24 -0
- package/templates/app-next/src/locales/home_en.json.example +6 -6
- package/templates/app-vite/index.html.example +1 -1
- package/templates/app-vite/src/locales/home_en.json.example +6 -6
- package/templates/functions-supabase/supabase/functions/.env.example +0 -2
- package/templates/root-consumer/.claude/commands/grill.md.example +86 -8
- package/templates/root-consumer/.dndev.secrets.example +32 -0
- package/templates/root-consumer/.gitignore.example +3 -0
- package/templates/root-consumer/AI.md.example +4 -0
- package/templates/root-consumer/entities/index.ts.example +2 -5
- package/templates/root-consumer/guides/dndev/COMPONENTS_ATOMIC.md.example +4 -0
- package/templates/root-consumer/guides/dndev/ENV_SETUP.md.example +23 -20
- package/templates/root-consumer/guides/dndev/INDEX.md.example +1 -0
- package/templates/root-consumer/guides/dndev/SETUP_BILLING.md.example +3 -7
- package/templates/root-consumer/guides/dndev/SETUP_CICD.md.example +115 -0
- package/templates/root-consumer/guides/dndev/SETUP_CRUD.md.example +41 -0
- package/templates/root-consumer/guides/dndev/SETUP_SUPABASE.md.example +13 -18
- package/templates/root-consumer/guides/dndev/SETUP_VERCEL.md.example +17 -12
- package/templates/root-consumer/guides/dndev/advanced/COOKIE_REFERENCE.md.example +252 -252
- package/templates/root-consumer/guides/dndev/advanced/VERSION_CONTROL.md.example +174 -174
- package/templates/root-consumer/guides/wai-way/WAI_WAY_CLI.md.example +185 -251
- package/templates/root-consumer/guides/wai-way/agents/extractor.md.example +26 -8
- package/templates/root-consumer/guides/wai-way/blueprints/0_brainstorm.md.example +66 -49
- package/templates/root-consumer/guides/wai-way/blueprints/1_scaffold.md.example +6 -5
- package/templates/root-consumer/guides/wai-way/blueprints/2_entities.md.example +9 -9
- package/templates/root-consumer/guides/wai-way/blueprints/3_compose.md.example +1 -1
- package/templates/root-consumer/guides/wai-way/blueprints/4_configure.md.example +7 -6
- package/templates/root-consumer/guides/wai-way/context_map.json.example +51 -20
- package/templates/root-consumer/guides/wai-way/hld_template.md.example +138 -0
- package/templates/root-consumer/guides/wai-way/lld_template.md.example +103 -0
- package/templates/root-consumer/guides/wai-way/prd_template.md.example +140 -0
- /package/templates/{root-consumer → app-demo}/entities/Contact.ts.example +0 -0
- /package/templates/{root-consumer → app-demo}/entities/demo.ts.example +0 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Terminal sidecar — real PTY via node-pty + WebSocket.
|
|
3
|
+
* Runs under Node (not Bun) to guarantee node-pty native addon works.
|
|
4
|
+
*
|
|
5
|
+
* @description Self-contained process: owns a WebSocket server + PTY lifecycle.
|
|
6
|
+
* Spawned by DndevPlugin.js with: spawn('node', [thisFile], { cwd: appRoot })
|
|
7
|
+
*
|
|
8
|
+
* Protocol (JSON over WebSocket):
|
|
9
|
+
* Client → Server: start, input, resize, kill
|
|
10
|
+
* Server → Client: started, output, exit, error
|
|
11
|
+
*
|
|
12
|
+
* @env {string} TERMINAL_PORT - WebSocket port (default: 24681)
|
|
13
|
+
* @env {string} TERMINAL_CWD - Working directory for shells (default: process.cwd())
|
|
14
|
+
* @env {string} TERMINAL_SHELL - Override shell binary (e.g. 'pwsh.exe', 'zsh')
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
'use strict';
|
|
18
|
+
|
|
19
|
+
const path = require('path');
|
|
20
|
+
const { createRequire } = require('module');
|
|
21
|
+
|
|
22
|
+
const appRequire = createRequire(path.join(__dirname, 'package.json'));
|
|
23
|
+
const pty = appRequire('node-pty');
|
|
24
|
+
const { WebSocketServer } = appRequire('ws');
|
|
25
|
+
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// SHELL CONFIG — override via env TERMINAL_SHELL or edit defaults below
|
|
28
|
+
// ============================================================================
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the shell binary to spawn.
|
|
32
|
+
* Priority: TERMINAL_SHELL env > platform default
|
|
33
|
+
*
|
|
34
|
+
* Platform defaults:
|
|
35
|
+
* Windows → wsl.exe (Linux shell inside Windows via WSL)
|
|
36
|
+
* macOS → zsh
|
|
37
|
+
* Linux → $SHELL or bash
|
|
38
|
+
* @returns {string} Shell binary path or name
|
|
39
|
+
*/
|
|
40
|
+
function resolveShell() {
|
|
41
|
+
if (process.env.TERMINAL_SHELL) return process.env.TERMINAL_SHELL;
|
|
42
|
+
|
|
43
|
+
if (process.platform === 'win32') {
|
|
44
|
+
// Default: WSL (Linux shell inside Windows)
|
|
45
|
+
// To use PowerShell instead, uncomment ONE of:
|
|
46
|
+
// return 'pwsh.exe'; // PowerShell 7+ (recommended)
|
|
47
|
+
// return 'powershell.exe'; // Windows PowerShell 5.1 (legacy)
|
|
48
|
+
return 'wsl.exe';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return process.env.SHELL || '/bin/bash';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Build a clean env for the child shell.
|
|
56
|
+
* Strips node/bun/turbo internals. On Windows, ensures HOME/SHELL aren't 'undefined'.
|
|
57
|
+
* @returns {Record<string, string>}
|
|
58
|
+
*/
|
|
59
|
+
function buildCleanEnv() {
|
|
60
|
+
const cleanEnv = Object.fromEntries(
|
|
61
|
+
Object.entries(process.env).filter(([k, v]) =>
|
|
62
|
+
v !== undefined &&
|
|
63
|
+
!k.startsWith('NODE_CHANNEL') &&
|
|
64
|
+
!k.startsWith('TURBO_') &&
|
|
65
|
+
!k.startsWith('BUN_') &&
|
|
66
|
+
k !== 'NODE_OPTIONS' &&
|
|
67
|
+
k !== 'FORCE_COLOR'
|
|
68
|
+
)
|
|
69
|
+
);
|
|
70
|
+
cleanEnv.FORCE_COLOR = '1';
|
|
71
|
+
cleanEnv.TERM = 'xterm-256color';
|
|
72
|
+
|
|
73
|
+
// Windows: HOME and SHELL don't exist natively — set sane defaults
|
|
74
|
+
if (process.platform === 'win32') {
|
|
75
|
+
if (!cleanEnv.HOME) cleanEnv.HOME = cleanEnv.USERPROFILE || '';
|
|
76
|
+
if (!cleanEnv.SHELL) delete cleanEnv.SHELL;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return cleanEnv;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolve cwd for the shell.
|
|
84
|
+
* IMPORTANT: cwd is passed to node-pty's spawn(), which runs on the host OS.
|
|
85
|
+
* On Windows, this MUST be a native Windows path (e.g. C:\ws\dndev).
|
|
86
|
+
* WSL automatically maps the Windows cwd to /mnt/c/... inside the Linux shell.
|
|
87
|
+
* @returns {string} Working directory path (native OS path)
|
|
88
|
+
*/
|
|
89
|
+
function resolveCwd() {
|
|
90
|
+
const cwd = process.env.TERMINAL_CWD;
|
|
91
|
+
if (!cwd) {
|
|
92
|
+
throw new Error('TERMINAL_CWD env is required — set it in DndevPlugin.js');
|
|
93
|
+
}
|
|
94
|
+
return cwd;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Validate a tab name — prevents log injection and memory abuse.
|
|
99
|
+
* @param {unknown} tab
|
|
100
|
+
* @returns {boolean}
|
|
101
|
+
*/
|
|
102
|
+
function isValidTab(tab) {
|
|
103
|
+
return typeof tab === 'string' && tab.length > 0 && tab.length <= 64 && /^[\w-]+$/.test(tab);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Clamp cols/rows to safe integer ranges for node-pty.
|
|
108
|
+
* @param {unknown} value
|
|
109
|
+
* @param {number} fallback
|
|
110
|
+
* @param {number} [min=1]
|
|
111
|
+
* @param {number} [max=500]
|
|
112
|
+
* @returns {number}
|
|
113
|
+
*/
|
|
114
|
+
function clampDimension(value, fallback, min = 1, max = 500) {
|
|
115
|
+
const n = typeof value === 'number' ? Math.floor(value) : fallback;
|
|
116
|
+
return Math.max(min, Math.min(max, n));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// ============================================================================
|
|
120
|
+
// CRASH GUARD — auto-respawn on immediate PTY death (ConPTY race condition)
|
|
121
|
+
// ============================================================================
|
|
122
|
+
|
|
123
|
+
/** @type {number} If PTY dies within this window, treat as crash */
|
|
124
|
+
const CRASH_THRESHOLD_MS = 3000;
|
|
125
|
+
/** @type {number} Max auto-respawn retries per tab */
|
|
126
|
+
const MAX_RETRIES = 2;
|
|
127
|
+
/** @type {Map<string, { count: number }>} Track crash retries per tab */
|
|
128
|
+
const spawnAttempts = new Map();
|
|
129
|
+
/** @type {Set<string>} Tabs killed intentionally — suppress duplicate onExit broadcast */
|
|
130
|
+
const killedTabs = new Set();
|
|
131
|
+
/** @type {Set<string>} Tabs with a pending crash-guard respawn timer */
|
|
132
|
+
const pendingRespawns = new Set();
|
|
133
|
+
|
|
134
|
+
// ============================================================================
|
|
135
|
+
// INIT
|
|
136
|
+
// ============================================================================
|
|
137
|
+
|
|
138
|
+
// Guard: node-pty requires real Node, not Bun
|
|
139
|
+
if (typeof globalThis.Bun !== 'undefined') {
|
|
140
|
+
console.error('[sidecar] FATAL: running under Bun — node-pty requires real Node. execPath=' + process.execPath);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const PORT = parseInt(process.env.TERMINAL_PORT || '24681', 10);
|
|
145
|
+
if (!Number.isFinite(PORT) || PORT < 1 || PORT > 65535) {
|
|
146
|
+
console.error(`[sidecar] FATAL: invalid TERMINAL_PORT: ${process.env.TERMINAL_PORT}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
console.error('[sidecar] Node ' + process.version + ' pid=' + process.pid);
|
|
151
|
+
|
|
152
|
+
/** @type {Map<string, import('node-pty').IPty>} Active PTY processes by tab id */
|
|
153
|
+
const ptys = new Map();
|
|
154
|
+
const wss = new WebSocketServer({ port: PORT });
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Broadcast a JSON message to all connected WebSocket clients.
|
|
158
|
+
* @param {Record<string, unknown>} msg
|
|
159
|
+
*/
|
|
160
|
+
function broadcast(msg) {
|
|
161
|
+
const data = JSON.stringify(msg);
|
|
162
|
+
for (const client of wss.clients) {
|
|
163
|
+
if (client.readyState === 1) client.send(data);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Spawn a PTY for the given tab. Handles crash-respawn internally.
|
|
169
|
+
* Wrapped in try/catch so failures in the setTimeout retry path don't crash the sidecar.
|
|
170
|
+
* @param {string} tab - Tab identifier
|
|
171
|
+
* @param {number} cols - Terminal columns
|
|
172
|
+
* @param {number} rows - Terminal rows
|
|
173
|
+
*/
|
|
174
|
+
function spawnPty(tab, cols, rows) {
|
|
175
|
+
try {
|
|
176
|
+
const shell = resolveShell();
|
|
177
|
+
const cleanEnv = buildCleanEnv();
|
|
178
|
+
const cwd = resolveCwd();
|
|
179
|
+
const safeCols = clampDimension(cols, 120);
|
|
180
|
+
const safeRows = clampDimension(rows, 30);
|
|
181
|
+
|
|
182
|
+
console.error(`[sidecar] spawning: ${shell} cwd=${cwd} cols=${safeCols} rows=${safeRows}`);
|
|
183
|
+
|
|
184
|
+
const p = pty.spawn(shell, [], {
|
|
185
|
+
name: 'xterm-256color',
|
|
186
|
+
cols: safeCols,
|
|
187
|
+
rows: safeRows,
|
|
188
|
+
cwd,
|
|
189
|
+
env: cleanEnv,
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
const spawnedAt = Date.now();
|
|
193
|
+
console.error(`[sidecar] PTY "${tab}" spawned: pid=${p.pid}`);
|
|
194
|
+
ptys.set(tab, p);
|
|
195
|
+
broadcast({ type: 'started', tab });
|
|
196
|
+
|
|
197
|
+
p.onData((data) => {
|
|
198
|
+
broadcast({ type: 'output', tab, data });
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
p.onExit(({ exitCode, signal }) => {
|
|
202
|
+
console.error(`[sidecar] PTY "${tab}" exited: code=${exitCode} signal=${signal} pid=${p.pid}`);
|
|
203
|
+
ptys.delete(tab);
|
|
204
|
+
|
|
205
|
+
// If killed intentionally via 'kill' command, the handler already broadcast 'exit'
|
|
206
|
+
if (killedTabs.has(tab)) {
|
|
207
|
+
killedTabs.delete(tab);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const livedMs = Date.now() - spawnedAt;
|
|
212
|
+
|
|
213
|
+
// Crash guard: if the PTY died almost instantly, auto-respawn
|
|
214
|
+
if (livedMs < CRASH_THRESHOLD_MS && exitCode !== 0) {
|
|
215
|
+
const attempts = spawnAttempts.get(tab) || { count: 0 };
|
|
216
|
+
attempts.count++;
|
|
217
|
+
spawnAttempts.set(tab, attempts);
|
|
218
|
+
|
|
219
|
+
if (attempts.count <= MAX_RETRIES) {
|
|
220
|
+
console.error(`[sidecar] PTY "${tab}" crashed after ${livedMs}ms — auto-respawn ${attempts.count}/${MAX_RETRIES}`);
|
|
221
|
+
pendingRespawns.add(tab);
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
pendingRespawns.delete(tab);
|
|
224
|
+
// If client already reconnected during the delay, skip
|
|
225
|
+
if (ptys.has(tab)) return;
|
|
226
|
+
spawnPty(tab, safeCols, safeRows);
|
|
227
|
+
}, 300);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
console.error(`[sidecar] PTY "${tab}" exhausted ${MAX_RETRIES} retries — giving up`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Normal exit or retries exhausted — notify client
|
|
234
|
+
spawnAttempts.delete(tab);
|
|
235
|
+
broadcast({ type: 'exit', tab, code: exitCode });
|
|
236
|
+
});
|
|
237
|
+
} catch (err) {
|
|
238
|
+
console.error(`[sidecar] PTY "${tab}" spawn failed:`, err.message);
|
|
239
|
+
ptys.delete(tab);
|
|
240
|
+
spawnAttempts.delete(tab);
|
|
241
|
+
broadcast({ type: 'error', tab, message: `Spawn failed: ${err.message}` });
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ============================================================================
|
|
246
|
+
// WEBSOCKET SERVER
|
|
247
|
+
// ============================================================================
|
|
248
|
+
|
|
249
|
+
wss.on('connection', (ws) => {
|
|
250
|
+
// Tell new client about already-running tabs
|
|
251
|
+
for (const tab of ptys.keys()) {
|
|
252
|
+
ws.send(JSON.stringify({ type: 'started', tab }));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
ws.on('message', (raw) => {
|
|
256
|
+
try {
|
|
257
|
+
const msg = JSON.parse(raw.toString());
|
|
258
|
+
|
|
259
|
+
// Validate tab name on all messages that carry one
|
|
260
|
+
if (msg.tab !== undefined && !isValidTab(msg.tab)) return;
|
|
261
|
+
|
|
262
|
+
switch (msg.type) {
|
|
263
|
+
case 'start': {
|
|
264
|
+
if (ptys.has(msg.tab)) {
|
|
265
|
+
ws.send(JSON.stringify({ type: 'started', tab: msg.tab }));
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
// If crash guard is about to respawn, let client take over instead
|
|
269
|
+
if (pendingRespawns.has(msg.tab)) {
|
|
270
|
+
pendingRespawns.delete(msg.tab);
|
|
271
|
+
}
|
|
272
|
+
spawnAttempts.delete(msg.tab);
|
|
273
|
+
spawnPty(
|
|
274
|
+
msg.tab,
|
|
275
|
+
clampDimension(msg.cols, 120),
|
|
276
|
+
clampDimension(msg.rows, 30),
|
|
277
|
+
);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
case 'input':
|
|
281
|
+
ptys.get(msg.tab)?.write(msg.data);
|
|
282
|
+
break;
|
|
283
|
+
case 'resize': {
|
|
284
|
+
const p = ptys.get(msg.tab);
|
|
285
|
+
if (p) {
|
|
286
|
+
p.resize(
|
|
287
|
+
clampDimension(msg.cols, 80),
|
|
288
|
+
clampDimension(msg.rows, 24),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
break;
|
|
292
|
+
}
|
|
293
|
+
case 'kill': {
|
|
294
|
+
const p = ptys.get(msg.tab);
|
|
295
|
+
if (p) {
|
|
296
|
+
killedTabs.add(msg.tab);
|
|
297
|
+
p.kill();
|
|
298
|
+
ptys.delete(msg.tab);
|
|
299
|
+
}
|
|
300
|
+
spawnAttempts.delete(msg.tab);
|
|
301
|
+
broadcast({ type: 'exit', tab: msg.tab, code: -1 });
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
console.error('[sidecar] message error:', err.message);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
wss.on('listening', () => {
|
|
312
|
+
const msg = `[dndev] terminal ready on ws://localhost:${PORT}`;
|
|
313
|
+
console.log(msg);
|
|
314
|
+
if (process.send) process.send({ type: 'ready', port: PORT });
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
wss.on('error', (err) => {
|
|
318
|
+
console.error('[sidecar] ws server error:', err.message);
|
|
319
|
+
if (err.code === 'EADDRINUSE') {
|
|
320
|
+
console.error(`[sidecar] port ${PORT} in use — exiting (DndevPlugin will retry)`);
|
|
321
|
+
process.exit(1);
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// ============================================================================
|
|
326
|
+
// CLEANUP
|
|
327
|
+
// ============================================================================
|
|
328
|
+
|
|
329
|
+
function cleanup() {
|
|
330
|
+
for (const [, p] of ptys) {
|
|
331
|
+
try { p.kill(); } catch { /* already dead */ }
|
|
332
|
+
}
|
|
333
|
+
ptys.clear();
|
|
334
|
+
wss.close(() => process.exit(0));
|
|
335
|
+
// Fallback if wss.close hangs
|
|
336
|
+
setTimeout(() => process.exit(0), 1000);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
process.on('SIGTERM', cleanup);
|
|
340
|
+
process.on('SIGINT', cleanup);
|
|
341
|
+
process.on('disconnect', cleanup);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineViteConfig } from '@donotdev/core/vite';
|
|
2
|
+
import { appConfig } from './src/config/app';
|
|
3
|
+
|
|
4
|
+
export default defineViteConfig({
|
|
5
|
+
appConfig,
|
|
6
|
+
dndev: true,
|
|
7
|
+
debug: false,
|
|
8
|
+
optimizeDeps: {
|
|
9
|
+
include: [
|
|
10
|
+
'@dnd-kit/core',
|
|
11
|
+
'@dnd-kit/sortable',
|
|
12
|
+
'@dnd-kit/utilities',
|
|
13
|
+
'react-markdown',
|
|
14
|
+
'remark-gfm'
|
|
15
|
+
],
|
|
16
|
+
},
|
|
17
|
+
server: {
|
|
18
|
+
port: 3174,
|
|
19
|
+
https: false,
|
|
20
|
+
watch: {
|
|
21
|
+
ignored: ['**/.dndev/**'],
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"hero": {
|
|
3
|
-
"title": "Welcome to DnDev",
|
|
4
|
-
"subtitle": "Your app is ready. Explore the framework patterns below."
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"hero": {
|
|
3
|
+
"title": "Welcome to DnDev",
|
|
4
|
+
"subtitle": "Your app is ready. Explore the framework patterns below."
|
|
5
|
+
}
|
|
6
|
+
}
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
box-sizing: border-box;
|
|
44
44
|
}
|
|
45
45
|
body {
|
|
46
|
-
font-family:
|
|
46
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
|
|
47
47
|
line-height: 1.5;
|
|
48
48
|
-webkit-font-smoothing: antialiased;
|
|
49
49
|
-moz-osx-font-smoothing: grayscale;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
{
|
|
2
|
-
"hero": {
|
|
3
|
-
"title": "Welcome to DnDev",
|
|
4
|
-
"subtitle": "Your app is ready. Explore the framework patterns below."
|
|
5
|
-
}
|
|
6
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"hero": {
|
|
3
|
+
"title": "Welcome to DnDev",
|
|
4
|
+
"subtitle": "Your app is ready. Explore the framework patterns below."
|
|
5
|
+
}
|
|
6
|
+
}
|
|
@@ -4,10 +4,8 @@
|
|
|
4
4
|
# Get these from: https://supabase.com/dashboard > your project > Settings
|
|
5
5
|
#
|
|
6
6
|
# Secret key: Settings > API > service_role key (or "Secret key" on new projects)
|
|
7
|
-
# DB URL: Settings > Database > Connection string > URI
|
|
8
7
|
#
|
|
9
8
|
# These are SERVER-SIDE ONLY. Never expose in client code.
|
|
10
9
|
# =============================================================================
|
|
11
10
|
|
|
12
11
|
SUPABASE_SECRET_KEY=
|
|
13
|
-
SUPABASE_DB_URL=
|
|
@@ -16,15 +16,93 @@ Evaluate for:
|
|
|
16
16
|
- Missing or incorrect TypeScript types
|
|
17
17
|
- @donotdev component usage without `lookup_symbol` verification
|
|
18
18
|
|
|
19
|
-
Output format — structured critique, one item per line:
|
|
20
|
-
```
|
|
21
|
-
[BLOCKER] <file>:<line> — <problem>
|
|
22
|
-
[WARN] <file>:<line> — <problem>
|
|
23
|
-
[NOTE] <file>:<line> — <problem>
|
|
24
|
-
```
|
|
25
|
-
|
|
26
19
|
Rules:
|
|
27
20
|
- NEVER suggest fixes. Report problems only.
|
|
28
21
|
- NEVER skip BLOCKER items to be polite.
|
|
29
22
|
- If there are no issues at a severity level, omit that level entirely.
|
|
30
|
-
-
|
|
23
|
+
- **Quality > quantity. 15 real issues >>> 60 padded items.**
|
|
24
|
+
|
|
25
|
+
## Output — 3-step lifecycle
|
|
26
|
+
|
|
27
|
+
The grill report is a **living queue**. Each run cleans up resolved items, archives them, then appends new findings.
|
|
28
|
+
|
|
29
|
+
### Step 1 — Clean up + archive
|
|
30
|
+
|
|
31
|
+
Read existing `.dndev/grill-report.json`. If it exists and has items:
|
|
32
|
+
|
|
33
|
+
1. Separate items into **resolved** (`status` = `fixed`, `resolved`, or `rejected`) and **surviving** (`pending`, `fixing`, or any other status).
|
|
34
|
+
2. Append a history entry to `.dndev/grill-history.json` (read existing array or start with `[]`):
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"date": "<now ISO>",
|
|
38
|
+
"gitSha": "<short git HEAD>",
|
|
39
|
+
"target": "<previous report target>",
|
|
40
|
+
"verdict": "<previous report verdict>",
|
|
41
|
+
"blockers": <previous blocker count>,
|
|
42
|
+
"warnings": <previous warning count>,
|
|
43
|
+
"notes": <previous note count>,
|
|
44
|
+
"total": <previous items.length>,
|
|
45
|
+
"resolved": <resolved count>,
|
|
46
|
+
"carried": <surviving count>
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
3. Keep the surviving items — they carry over into the updated report.
|
|
50
|
+
|
|
51
|
+
If no existing report or no items, skip this step.
|
|
52
|
+
|
|
53
|
+
### Step 2 — Scan
|
|
54
|
+
|
|
55
|
+
Produce new findings from the target files. **Deduplicate** against surviving items from Step 1: if a new finding matches an existing item on `file` + `line` + `issue` (fuzzy match on issue text), skip it.
|
|
56
|
+
|
|
57
|
+
### Step 3 — Write report
|
|
58
|
+
|
|
59
|
+
Merge surviving items from Step 1 + new findings from Step 2. Re-number `id` fields sequentially starting at 1. Write to `.dndev/grill-report.json`:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"generated": "<now ISO>",
|
|
64
|
+
"gitSha": "<short git HEAD>",
|
|
65
|
+
"target": "<description of what was reviewed>",
|
|
66
|
+
"verdict": "SHIP" | "DO NOT SHIP",
|
|
67
|
+
"items": [
|
|
68
|
+
{
|
|
69
|
+
"id": 1,
|
|
70
|
+
"severity": "blocker" | "warn" | "note",
|
|
71
|
+
"file": "src/pages/Foo.tsx",
|
|
72
|
+
"line": 42,
|
|
73
|
+
"issue": "Short description of the problem",
|
|
74
|
+
"context": "Optional: why this matters, what could go wrong",
|
|
75
|
+
"category": "security" | "architecture" | "edge-case" | "types" | "dead-code" | "consistency" | "dx" | "docs" | "performance",
|
|
76
|
+
"status": "pending",
|
|
77
|
+
"verified": true
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Verdict is based on the **merged** items (carried + new). If any blocker exists → DO NOT SHIP.
|
|
84
|
+
|
|
85
|
+
### Field rules
|
|
86
|
+
|
|
87
|
+
- `gitSha` — Run `git rev-parse --short HEAD`. Required.
|
|
88
|
+
- `verified` — Always `true` in final output.
|
|
89
|
+
- New items always get `status: "pending"`. Carried items keep their existing status.
|
|
90
|
+
|
|
91
|
+
### Fixing findings
|
|
92
|
+
|
|
93
|
+
When asked to fix grill findings (not during a scan — during a separate fix request):
|
|
94
|
+
|
|
95
|
+
1. Apply the code fix.
|
|
96
|
+
2. Update `.dndev/grill-report.json`: set `status: "fixed"` on each resolved item.
|
|
97
|
+
3. Set `lastReviewed` to current ISO timestamp.
|
|
98
|
+
|
|
99
|
+
This ensures the report reflects reality. Next grill run archives fixed items to history.
|
|
100
|
+
|
|
101
|
+
### General rules
|
|
102
|
+
|
|
103
|
+
- NEVER suggest fixes. Report problems only — during a scan.
|
|
104
|
+
- NEVER skip BLOCKER items.
|
|
105
|
+
- **Quality > quantity. 15 real issues >>> 60 padded items** (for new findings — carried items don't count toward this cap).
|
|
106
|
+
- Group by file, sort by line number.
|
|
107
|
+
- After writing, print: `Cleaned N resolved items → history. Carried M items. Added K new findings. Total: T items.`
|
|
108
|
+
- Print verdict: SHIP / DO NOT SHIP.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
# .dndev.secrets — All secret keys in one place
|
|
3
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
#
|
|
5
|
+
# This file is gitignored. Copy from .dndev.secrets.example and fill in values.
|
|
6
|
+
# Resolution order: process.env (CI) → .dndev.secrets (local) → legacy paths
|
|
7
|
+
#
|
|
8
|
+
# For public config (VITE_*, NEXT_PUBLIC_*), use apps/<app>/.env instead.
|
|
9
|
+
# For Firebase service account, keep service-account-key.json as-is.
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
{{#vercel}}
|
|
13
|
+
# ── Vercel ──────────────────────────────────────────────────────────────────
|
|
14
|
+
# Get from: https://vercel.com/account/tokens
|
|
15
|
+
VERCEL_TOKEN=
|
|
16
|
+
|
|
17
|
+
{{/vercel}}
|
|
18
|
+
{{#supabase}}
|
|
19
|
+
# ── Supabase ────────────────────────────────────────────────────────────────
|
|
20
|
+
# Secret key: https://supabase.com/dashboard → Settings → API → service_role key
|
|
21
|
+
# Access token: https://supabase.com/dashboard/account/tokens (needed for DB migrations)
|
|
22
|
+
SUPABASE_SECRET_KEY=
|
|
23
|
+
SUPABASE_ACCESS_TOKEN=
|
|
24
|
+
|
|
25
|
+
{{/supabase}}
|
|
26
|
+
{{#stripe}}
|
|
27
|
+
# ── Stripe ──────────────────────────────────────────────────────────────────
|
|
28
|
+
# Get from: https://dashboard.stripe.com/apikeys (secret key)
|
|
29
|
+
# https://dashboard.stripe.com/webhooks (webhook secret)
|
|
30
|
+
STRIPE_SECRET_KEY=
|
|
31
|
+
STRIPE_WEBHOOK_SECRET=
|
|
32
|
+
{{/stripe}}
|
|
@@ -20,6 +20,9 @@ stats.html
|
|
|
20
20
|
# Note: .env.local and .env.*.local are gitignored (local overrides)
|
|
21
21
|
# .env.production.local is gitignored (production secrets)
|
|
22
22
|
|
|
23
|
+
# Centralized secrets file (all secret keys in one place)
|
|
24
|
+
.dndev.secrets
|
|
25
|
+
|
|
23
26
|
# IDE files
|
|
24
27
|
.vscode/*
|
|
25
28
|
!.vscode/extensions.json
|
|
@@ -88,6 +88,10 @@ Run `dndev --help` for the full command list.
|
|
|
88
88
|
- **RTL safe** — use `start`/`end`, never `left`/`right`
|
|
89
89
|
- **Import order** — React → vendors → @donotdev → relative
|
|
90
90
|
- **Import convention** — always `@donotdev/<pkg>` top-level, never sub-paths
|
|
91
|
+
- **NO native HTML elements** — never use `<div>`, `<span>`, `<p>`, `<h1-6>`, `<button>`, `<a>`, `<input>`, `<textarea>`, `<select>`, `<form>`, `<table>`, `<ul>`, `<ol>`, `<li>`, `<img>`, `<section>`, `<nav>`, `<header>`, `<footer>` in @donotdev files. Use framework components: Stack, Card, Text, Button, Link, TextInput, Select, Form, DataTable, List, Image, Section, Navigation, Header, Footer. See `get_guide("COMPONENTS_ATOMIC")`
|
|
92
|
+
- **NO custom CSS** — never create `.css`, `.module.css`, `.scss`, `.sass`, or `.less` files. All styling is done through framework component props, variant tokens, and theme configuration
|
|
93
|
+
- **NO custom components** — never create files in `components/`. Compose pages using framework components only. If a component is missing, say what you need and STOP
|
|
94
|
+
- **NO inventing props** — only use props returned by `lookup_symbol()`. Common hallucination traps that DO NOT exist: `size`, `tone`, `color`, `padding`, `margin`, `spacing`, `columns`, `background`, `fields`
|
|
91
95
|
- **Framework-first** — if something's missing, say what and stop
|
|
92
96
|
- **No .md file creation** — never create documentation/analysis/summary .md files unless explicitly asked. Session notes go in `.dndev/`
|
|
93
97
|
- **Follow existing patterns** — the scaffolded files ARE your documentation
|
|
@@ -2,17 +2,14 @@
|
|
|
2
2
|
* @fileoverview Entity Exports
|
|
3
3
|
*
|
|
4
4
|
* Export all your entities from here for easy importing.
|
|
5
|
+
* See entities/examples/ for reference entity definitions.
|
|
5
6
|
*
|
|
6
7
|
* USAGE:
|
|
7
8
|
* ```tsx
|
|
8
|
-
* import {
|
|
9
|
+
* import { customerEntity } from '../entities';
|
|
9
10
|
* ```
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
|
-
// Scaffolded examples — rename or replace with your own entities:
|
|
13
|
-
export { contactEntity } from './Contact';
|
|
14
|
-
export { productEntity } from './ExampleEntity';
|
|
15
|
-
|
|
16
13
|
// Add your entities:
|
|
17
14
|
// export { customerEntity } from './Customer';
|
|
18
15
|
// export { orderEntity } from './Order';
|
|
@@ -38,6 +38,7 @@ These are the only ways one should handle layout to get to quick results functio
|
|
|
38
38
|
- **ActionButton** - Button with async action handling and loading states.
|
|
39
39
|
- **FileButton** - Button that triggers file input selection.
|
|
40
40
|
- **PortalButton** - Button that opens content in a portal overlay.
|
|
41
|
+
- **ButtonGroup** - Visual grouping of buttons with joined borders. Supports horizontal/vertical orientation, variant propagation, and items-based API.
|
|
41
42
|
|
|
42
43
|
---
|
|
43
44
|
|
|
@@ -53,6 +54,7 @@ These are the only ways one should handle layout to get to quick results functio
|
|
|
53
54
|
- **Combobox** - Searchable combobox with autocomplete functionality.
|
|
54
55
|
- **Switch** - Toggle switch component.
|
|
55
56
|
- **Slider** - Range slider for numeric input.
|
|
57
|
+
- **ColorPicker** - Color selection with preset swatch grid, native color input, and ARIA radiogroup pattern. Built-in palettes: basic, material, pastel.
|
|
56
58
|
|
|
57
59
|
---
|
|
58
60
|
|
|
@@ -76,6 +78,7 @@ These are the only ways one should handle layout to get to quick results functio
|
|
|
76
78
|
- **Pagination** - Accessible pagination with page number generation and ellipsis.
|
|
77
79
|
- **Command** - Command palette component for keyboard navigation and search.
|
|
78
80
|
- **CommandDialog** - Command palette dialog optimized for Cmd+K navigation.
|
|
81
|
+
- **TreeView** - Hierarchical tree component with WAI-ARIA TreeView pattern. Supports expand/collapse, single/multi selection, keyboard navigation, type-ahead search, and connector lines.
|
|
79
82
|
|
|
80
83
|
---
|
|
81
84
|
|
|
@@ -110,6 +113,7 @@ These are the only ways one should handle layout to get to quick results functio
|
|
|
110
113
|
- **Portal** - Portal component for rendering outside DOM hierarchy.
|
|
111
114
|
- **VisuallyHidden** - Screen reader-only content component.
|
|
112
115
|
- **Slot** - Slot component for composition patterns.
|
|
116
|
+
- **SkipLink** - Accessibility skip-to-content links, visible only on keyboard focus. Supports multiple targets with smooth scroll and focus management.
|
|
113
117
|
|
|
114
118
|
---
|
|
115
119
|
|