@brunosps00/dev-workflow 1.6.0 → 1.7.1
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/.claude-plugin/plugin.json +2 -2
- package/README.md +2 -2
- package/lib/constants.js +2 -0
- package/lib/install-deps.js +37 -1
- package/lib/install-open-design.js +128 -0
- package/lib/utils.js +46 -6
- package/package.json +2 -2
- package/scaffold/en/commands/dw-help.md +3 -1
- package/scaffold/en/commands/dw-new-project.md +24 -21
- package/scaffold/en/commands/dw-open-design.md +324 -0
- package/scaffold/en/templates/project-onepager.md +12 -11
- package/scaffold/pt-br/commands/dw-help.md +3 -1
- package/scaffold/pt-br/commands/dw-new-project.md +24 -21
- package/scaffold/pt-br/commands/dw-open-design.md +323 -0
- package/scaffold/pt-br/templates/project-onepager.md +12 -11
- package/scaffold/scripts/open-design/capture-current.mjs +170 -0
- package/scaffold/scripts/open-design/gate-prototype.mjs +223 -0
- package/scaffold/skills/docker-compose-recipes/SKILL.md +6 -5
- package/scaffold/skills/docker-compose-recipes/references/compose-composition.md +7 -7
- package/scaffold/skills/docker-compose-recipes/references/env-conventions.md +3 -1
- package/scaffold/skills/docker-compose-recipes/references/healthcheck-patterns.md +4 -4
- package/scaffold/skills/docker-compose-recipes/services/mailpit.yml +4 -4
- package/scaffold/skills/docker-compose-recipes/services/postgres-pgvector.yml +31 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dev-workflow",
|
|
3
3
|
"description": "Standalone, à-la-carte skills from the dev-workflow pipeline: minimalism, search-first, simplification, verification, security review, and more.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.7.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bruno Santos"
|
|
7
7
|
},
|
|
8
|
-
"homepage": "https://github.com/
|
|
8
|
+
"homepage": "https://github.com/brunosps/dev-workflow",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"ai",
|
package/README.md
CHANGED
|
@@ -42,7 +42,7 @@ Exportable skills (no `.dw/` pipeline required): `dw-minimalism`, `dw-search-fir
|
|
|
42
42
|
|
|
43
43
|
## Commands
|
|
44
44
|
|
|
45
|
-
dev-workflow v1.
|
|
45
|
+
dev-workflow v1.7.0 ships **38 commands** organized into four tiers. Most users only invoke Tier 1 + Tier 2.
|
|
46
46
|
|
|
47
47
|
### Tier 1 — Gateway (3)
|
|
48
48
|
|
|
@@ -81,7 +81,7 @@ Use these when you want step-by-step control instead of `/dw-autopilot`.
|
|
|
81
81
|
| **`/dw-harness-audit`** | Deterministic health scorecard for the dev-workflow install, agents, wrappers, MCPs, and gates. |
|
|
82
82
|
| **`/dw-skill-health`** | Audits installed skills and agents for bloat, duplication, and missing references. |
|
|
83
83
|
| **`/dw-learn`** | Synthesizes durable decisions (confidence-tagged), bugfixes, deviations, and git history into atomic confidence-weighted **instincts** you approve at `.dw/memory/instincts/`; can propose constitution principles. On-demand and human-in-the-loop — **no always-on observer**. |
|
|
84
|
-
| **`/dw-new-project`** | Bootstrap a new project from empty directory. Stack interview, wraps official `create-*` tools
|
|
84
|
+
| **`/dw-new-project`** | Bootstrap a new project from an empty or docs-first directory while preserving existing documentation and prototypes. Stack interview includes Next.js + NestJS, Postgres + pgvector, pg-boss, Mailpit, dev topology, and wraps official `create-*` tools before composing dev infrastructure, `.env`, scripts, CI, and `.dw/rules/`. |
|
|
85
85
|
| **`/dw-dockerize`** | Reads existing project, detects stack + runtime deps, proposes Dockerfile + docker-compose for dev/prod with explicit trade-offs (Conservative/Balanced/Bold). |
|
|
86
86
|
| **`/dw-install-azure-skills`** | **Opt-in.** Clones curated Azure skills from [`MicrosoftDocs/Agent-Skills`](https://github.com/MicrosoftDocs/Agent-Skills) (CC-BY-4.0) into `.agents/skills/azure/` and registers the [Microsoft Learn MCP Server](https://learn.microsoft.com/en-us/training/support/mcp-get-started) (HTTP, no-auth). Interactive category selection (Compute / Data & Storage / AI & ML / Networking / Identity & Security / DevOps / Observability / Integration / Architecture / All). Re-run to refresh from upstream. Also available as CLI: `npx @brunosps00/dev-workflow install-azure-skills`. |
|
|
87
87
|
| **`/dw-install-aws-skills`** | **Opt-in.** Clones curated AWS skills from [`aws/agent-toolkit-for-aws`](https://github.com/aws/agent-toolkit-for-aws) (Apache 2.0) into `.agents/skills/aws/` and registers the unified [AWS MCP Server](https://docs.aws.amazon.com/aws-mcp/) (stdio via `uvx mcp-proxy-for-aws@latest`). **Requires `uv`, `aws cli ≥ 2.32.0`, and AWS credentials.** Interactive category selection (Core / Analytics / Database / EC2 / Migration / Networking / Operations / Security / Serverless / Storage / All). The agent gains `aws___call_aws` (executes 15,000+ AWS APIs) and `aws___run_script` (Python sandboxed) — review `.dw/references/aws-mcp-instructions.md` for the destructive-operations protocol. Also available as CLI: `npx @brunosps00/dev-workflow install-aws-skills [--region=<aws-region>]`. |
|
package/lib/constants.js
CHANGED
|
@@ -22,6 +22,7 @@ const COMMANDS = {
|
|
|
22
22
|
{ name: 'dw-intel', description: 'Codebase intelligence: query mode (default) answers questions citing .dw/intel/ + .dw/rules/; --build mode (re)builds the index.' },
|
|
23
23
|
{ name: 'dw-learn', description: 'Synthesizes durable decisions, bugfixes, deviations, and git history into atomic confidence-weighted instincts you approve; can propose constitution principles. On-demand and human-in-the-loop — no always-on observer.' },
|
|
24
24
|
{ name: 'dw-new-project', description: 'Interviews you about stack and infra, then scaffolds a working monorepo with docker-compose for dev, .env, scripts, CI, and seeded rules.' },
|
|
25
|
+
{ name: 'dw-open-design', userInvoked: true, description: 'Trigger when user asks to generate, iterate, or refactor/redesign headless Open Design HTML prototypes via the od CLI. Imports a target folder with isolated OD_DATA_DIR, refines briefs before dispatch, supports --refactor screenshots via --url/--viewports or --screenshot, runs web-prototype briefs with explicit --agent codex|claude, verifies files, captures JSONL audit logs, and stops for a dual >=9 visual gate.' },
|
|
25
26
|
{ name: 'dw-opportunities', description: 'Scans the installed project for product, UX, automation, refactor, and security opportunities, then routes each idea to the right next dw-* command.' },
|
|
26
27
|
{ name: 'dw-pause', description: 'Trigger when user says "pause work", "end session", or "save where we are". Consolidates open loops, decisions, blockers, and todos into .dw/STATE.md so the next session can resume.' },
|
|
27
28
|
{ name: 'dw-plan', description: 'Trigger when user has a feature idea and needs spec + architecture + tasks. Runs PRD → TechSpec → Tasks sequentially. Stages: prd / techspec / tasks; --from techspec; --council.' },
|
|
@@ -61,6 +62,7 @@ const COMMANDS = {
|
|
|
61
62
|
{ name: 'dw-intel', description: 'Inteligencia do codebase: modo query (default) responde citando .dw/intel/ + .dw/rules/; modo --build (re)constroi indice.' },
|
|
62
63
|
{ name: 'dw-learn', description: 'Sintetiza decisoes duraveis, bugfixes, deviations e historico git em instincts atomicos ponderados por confianca que voce aprova; pode propor principios de constitution. On-demand e human-in-the-loop — sem observador sempre-ligado.' },
|
|
63
64
|
{ name: 'dw-new-project', description: 'Entrevista voce sobre stack e infra, depois faz scaffold de um monorepo com docker-compose para dev, .env, scripts, CI e rules seed.' },
|
|
65
|
+
{ name: 'dw-open-design', userInvoked: true, description: 'Trigger quando usuario pede para gerar, iterar ou refatorar/redesenhar prototipos HTML headless do Open Design via od CLI. Importa pasta alvo com OD_DATA_DIR isolado, refina briefs antes de despachar, suporta prints --refactor via --url/--viewports ou --screenshot, roda briefs web-prototype com --agent codex|claude explicito, verifica arquivos, captura logs JSONL e para para gate visual dual >=9.' },
|
|
64
66
|
{ name: 'dw-opportunities', description: 'Escaneia o projeto instalado por oportunidades de produto, UX, automacao, refactor e seguranca, e roteia cada ideia para o proximo comando dw-* correto.' },
|
|
65
67
|
{ name: 'dw-pause', description: 'Trigger quando usuario diz "pausa o trabalho", "encerra a sessao" ou "salva onde paramos". Consolida pontas soltas, decisoes, bloqueios e todos em .dw/STATE.md para a proxima sessao retomar.' },
|
|
66
68
|
{ name: 'dw-plan', description: 'Trigger quando usuario tem ideia de feature e precisa spec + arquitetura + tasks. Roda PRD → TechSpec → Tasks sequencial. Stages: prd / techspec / tasks; --from techspec; --council.' },
|
package/lib/install-deps.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { execSync } = require('child_process');
|
|
2
|
+
const openDesign = require('./install-open-design');
|
|
2
3
|
|
|
3
4
|
function run() {
|
|
4
5
|
console.log('\n dev-workflow install-deps');
|
|
@@ -84,6 +85,13 @@ function run() {
|
|
|
84
85
|
'Verify after install: docker --version && docker compose version',
|
|
85
86
|
],
|
|
86
87
|
},
|
|
88
|
+
{
|
|
89
|
+
name: 'Open Design CLI (od)',
|
|
90
|
+
check: openDesign.check,
|
|
91
|
+
install: openDesign.install,
|
|
92
|
+
instructions: openDesign.instructions(),
|
|
93
|
+
sync: openDesign.syncProjectAssets,
|
|
94
|
+
},
|
|
87
95
|
];
|
|
88
96
|
|
|
89
97
|
let installed = 0;
|
|
@@ -113,12 +121,28 @@ function run() {
|
|
|
113
121
|
|
|
114
122
|
process.stdout.write(` Installing ${dep.name}...`);
|
|
115
123
|
try {
|
|
116
|
-
|
|
124
|
+
if (typeof dep.check === 'function' && dep.check()) {
|
|
125
|
+
console.log(' \x1b[32m✓ already installed\x1b[0m');
|
|
126
|
+
runDepSync(dep);
|
|
127
|
+
installed++;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (typeof dep.install === 'function') {
|
|
131
|
+
dep.install();
|
|
132
|
+
} else {
|
|
133
|
+
execSync(dep.install, { stdio: 'pipe', timeout: 300000 });
|
|
134
|
+
}
|
|
117
135
|
console.log(' \x1b[32m✓\x1b[0m');
|
|
136
|
+
runDepSync(dep);
|
|
118
137
|
installed++;
|
|
119
138
|
} catch (err) {
|
|
120
139
|
console.log(' \x1b[31m✗\x1b[0m');
|
|
121
140
|
console.log(` Error: ${err.message.split('\n')[0]}`);
|
|
141
|
+
if (Array.isArray(dep.instructions)) {
|
|
142
|
+
for (const line of dep.instructions) {
|
|
143
|
+
console.log(` ${line}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
122
146
|
failed++;
|
|
123
147
|
}
|
|
124
148
|
}
|
|
@@ -181,3 +205,15 @@ function printBrowserGuidance() {
|
|
|
181
205
|
}
|
|
182
206
|
|
|
183
207
|
module.exports = { run };
|
|
208
|
+
|
|
209
|
+
function runDepSync(dep) {
|
|
210
|
+
if (typeof dep.sync !== 'function') return;
|
|
211
|
+
try {
|
|
212
|
+
const result = dep.sync();
|
|
213
|
+
if (result && result.synced === false && result.reason) {
|
|
214
|
+
console.log(` (project assets not synced: ${result.reason})`);
|
|
215
|
+
}
|
|
216
|
+
} catch (err) {
|
|
217
|
+
console.log(` (project asset sync failed: ${err.message})`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
const { execFileSync } = require('child_process');
|
|
5
|
+
const { COMMANDS } = require('./constants');
|
|
6
|
+
const { copyDir, writeFile, log } = require('./utils');
|
|
7
|
+
const { generateWrappers } = require('./wrappers');
|
|
8
|
+
const { readInstallState } = require('./install-state');
|
|
9
|
+
|
|
10
|
+
const SCAFFOLD_DIR = path.join(__dirname, '..', 'scaffold');
|
|
11
|
+
|
|
12
|
+
const OPEN_DESIGN_REPO = 'https://github.com/nexu-io/open-design';
|
|
13
|
+
const OPEN_DESIGN_REF = '1cb7eae';
|
|
14
|
+
const OPEN_DESIGN_VENDOR_DIR = path.join(os.homedir(), '.dw', 'vendor', 'open-design');
|
|
15
|
+
|
|
16
|
+
function odBin(dir = OPEN_DESIGN_VENDOR_DIR) {
|
|
17
|
+
return path.join(dir, 'apps', 'daemon', 'bin', 'od.mjs');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function check() {
|
|
21
|
+
return fs.existsSync(odBin());
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function install() {
|
|
25
|
+
checkPrerequisites();
|
|
26
|
+
|
|
27
|
+
fs.mkdirSync(path.dirname(OPEN_DESIGN_VENDOR_DIR), { recursive: true });
|
|
28
|
+
if (!fs.existsSync(path.join(OPEN_DESIGN_VENDOR_DIR, '.git'))) {
|
|
29
|
+
if (fs.existsSync(OPEN_DESIGN_VENDOR_DIR)) {
|
|
30
|
+
throw new Error(`${OPEN_DESIGN_VENDOR_DIR} exists but is not a git checkout; move it aside and retry.`);
|
|
31
|
+
}
|
|
32
|
+
execFileSync('git', ['clone', OPEN_DESIGN_REPO, OPEN_DESIGN_VENDOR_DIR], { stdio: 'pipe', timeout: 600000 });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
execFileSync('git', ['fetch', '--tags', '--force', 'origin'], { cwd: OPEN_DESIGN_VENDOR_DIR, stdio: 'pipe', timeout: 600000 });
|
|
36
|
+
execFileSync('git', ['checkout', OPEN_DESIGN_REF], { cwd: OPEN_DESIGN_VENDOR_DIR, stdio: 'pipe', timeout: 120000 });
|
|
37
|
+
execFileSync('pnpm', ['install', '--frozen-lockfile'], { cwd: OPEN_DESIGN_VENDOR_DIR, stdio: 'pipe', timeout: 1200000 });
|
|
38
|
+
|
|
39
|
+
if (!check()) {
|
|
40
|
+
throw new Error(`Open Design install finished but ${odBin()} was not found.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Sync the dw-open-design command, scripts and harness wrappers into the
|
|
46
|
+
* current project. install-deps provisions the od CLI, but the skill lives in
|
|
47
|
+
* the project scaffold — without this, users only get the command after a
|
|
48
|
+
* manual `dev-workflow update` (the exact gap reported in the field).
|
|
49
|
+
*/
|
|
50
|
+
function syncProjectAssets(projectRoot = process.cwd()) {
|
|
51
|
+
const state = readInstallState(projectRoot);
|
|
52
|
+
if (!state) {
|
|
53
|
+
return { synced: false, reason: 'no dev-workflow install found in this project (run init/update first)' };
|
|
54
|
+
}
|
|
55
|
+
const lang = state.lang && COMMANDS[state.lang] ? state.lang : 'en';
|
|
56
|
+
const cmd = COMMANDS[lang].find((entry) => entry.name === 'dw-open-design')
|
|
57
|
+
|| COMMANDS.en.find((entry) => entry.name === 'dw-open-design');
|
|
58
|
+
if (!cmd) {
|
|
59
|
+
return { synced: false, reason: 'dw-open-design missing from command registry' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const commandSrc = path.join(SCAFFOLD_DIR, lang, 'commands', 'dw-open-design.md');
|
|
63
|
+
const commandDest = path.join(projectRoot, '.dw', 'commands', 'dw-open-design.md');
|
|
64
|
+
const status = writeFile(commandDest, fs.readFileSync(commandSrc, 'utf-8'), true);
|
|
65
|
+
log(status, commandDest);
|
|
66
|
+
|
|
67
|
+
const scriptsSrc = path.join(SCAFFOLD_DIR, 'scripts', 'open-design');
|
|
68
|
+
if (fs.existsSync(scriptsSrc)) {
|
|
69
|
+
copyDir(scriptsSrc, path.join(projectRoot, '.dw', 'scripts', 'open-design'), true);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
generateWrappers(projectRoot, [cmd], true);
|
|
73
|
+
return { synced: true };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function instructions() {
|
|
77
|
+
return [
|
|
78
|
+
'Open Design CLI is provisioned by dev-workflow into a managed checkout:',
|
|
79
|
+
` ${OPEN_DESIGN_VENDOR_DIR}`,
|
|
80
|
+
` repo: ${OPEN_DESIGN_REPO}`,
|
|
81
|
+
` pinned ref: ${OPEN_DESIGN_REF}`,
|
|
82
|
+
'',
|
|
83
|
+
'Prerequisites:',
|
|
84
|
+
' Node.js ~24: install with fnm/nvm/asdf, then verify: node -v',
|
|
85
|
+
' pnpm >=10.33: corepack enable && corepack prepare pnpm@10.33.2 --activate',
|
|
86
|
+
' git: required to clone the upstream monorepo',
|
|
87
|
+
'',
|
|
88
|
+
'Then run: dev-workflow install-deps',
|
|
89
|
+
'To use your own checkout instead of the managed one, set OD_CLI_DIR=/path/to/open-design.',
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function checkPrerequisites() {
|
|
94
|
+
const nodeVersion = process.versions.node;
|
|
95
|
+
const nodeMajor = Number(nodeVersion.split('.')[0]);
|
|
96
|
+
if (nodeMajor !== 24) {
|
|
97
|
+
throw new Error(`Open Design requires Node.js ~24; detected ${nodeVersion}.`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const pnpmRaw = execFileSync('pnpm', ['-v'], { stdio: 'pipe', timeout: 10000 }).toString().trim();
|
|
101
|
+
if (!isAtLeast(pnpmRaw, '10.33.0')) {
|
|
102
|
+
throw new Error(`Open Design requires pnpm >=10.33; detected ${pnpmRaw}.`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
execFileSync('git', ['--version'], { stdio: 'pipe', timeout: 10000 });
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function isAtLeast(actual, minimum) {
|
|
109
|
+
const a = actual.split('.').map((part) => Number(part));
|
|
110
|
+
const b = minimum.split('.').map((part) => Number(part));
|
|
111
|
+
for (let index = 0; index < Math.max(a.length, b.length); index += 1) {
|
|
112
|
+
const av = a[index] || 0;
|
|
113
|
+
const bv = b[index] || 0;
|
|
114
|
+
if (av > bv) return true;
|
|
115
|
+
if (av < bv) return false;
|
|
116
|
+
}
|
|
117
|
+
return true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
syncProjectAssets,
|
|
122
|
+
OPEN_DESIGN_REF,
|
|
123
|
+
OPEN_DESIGN_VENDOR_DIR,
|
|
124
|
+
check,
|
|
125
|
+
install,
|
|
126
|
+
instructions,
|
|
127
|
+
odBin,
|
|
128
|
+
};
|
package/lib/utils.js
CHANGED
|
@@ -90,6 +90,20 @@ function writeJson(filePath, value) {
|
|
|
90
90
|
fs.writeFileSync(filePath, JSON.stringify(value, null, 2) + '\n', 'utf-8');
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
+
function findStandaloneMarkers(content, marker) {
|
|
94
|
+
// Marker names also appear in explanatory prose, so only a full marker line delimits a block.
|
|
95
|
+
const escapedMarker = marker.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
96
|
+
const pattern = new RegExp(`^[\\t ]*${escapedMarker}[\\t ]*\\r?$`, 'gm');
|
|
97
|
+
const markers = [];
|
|
98
|
+
let match;
|
|
99
|
+
|
|
100
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
101
|
+
const start = match.index + match[0].indexOf(marker);
|
|
102
|
+
markers.push({ start, end: start + marker.length });
|
|
103
|
+
}
|
|
104
|
+
return markers;
|
|
105
|
+
}
|
|
106
|
+
|
|
93
107
|
/**
|
|
94
108
|
* Upsert a delimited block inside an existing file.
|
|
95
109
|
*
|
|
@@ -105,8 +119,19 @@ function upsertDelimitedBlock(filePath, blockContent, startMarker, endMarker) {
|
|
|
105
119
|
|
|
106
120
|
// The block content already includes its own start/end markers (so the scaffold
|
|
107
121
|
// file can be read verbatim). Validate that.
|
|
108
|
-
|
|
109
|
-
|
|
122
|
+
const blockStarts = findStandaloneMarkers(blockContent, startMarker);
|
|
123
|
+
const blockEnds = findStandaloneMarkers(blockContent, endMarker);
|
|
124
|
+
if (blockStarts.length === 0 || blockEnds.length === 0) {
|
|
125
|
+
throw new Error(`Block content for ${filePath} is missing standalone markers`);
|
|
126
|
+
}
|
|
127
|
+
if (blockStarts.length > 1 || blockEnds.length > 1) {
|
|
128
|
+
throw new Error(`Block content for ${filePath} has duplicate standalone markers`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const blockStart = blockStarts[0];
|
|
132
|
+
const blockEnd = blockEnds[0];
|
|
133
|
+
if (blockEnd.start < blockStart.end) {
|
|
134
|
+
throw new Error(`Block content for ${filePath} is missing standalone markers`);
|
|
110
135
|
}
|
|
111
136
|
|
|
112
137
|
if (!exists) {
|
|
@@ -115,17 +140,32 @@ function upsertDelimitedBlock(filePath, blockContent, startMarker, endMarker) {
|
|
|
115
140
|
}
|
|
116
141
|
|
|
117
142
|
const current = fs.readFileSync(filePath, 'utf-8');
|
|
118
|
-
const
|
|
143
|
+
const currentStarts = findStandaloneMarkers(current, startMarker);
|
|
144
|
+
const currentEnds = findStandaloneMarkers(current, endMarker);
|
|
119
145
|
|
|
120
|
-
if (
|
|
146
|
+
if (currentStarts.length === 0 && currentEnds.length === 0) {
|
|
121
147
|
const separator = current.endsWith('\n') ? '\n' : '\n\n';
|
|
122
148
|
const next = current + separator + (blockContent.endsWith('\n') ? blockContent : blockContent + '\n');
|
|
123
149
|
fs.writeFileSync(filePath, next, 'utf-8');
|
|
124
150
|
return 'updated';
|
|
125
151
|
}
|
|
126
152
|
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
if (currentStarts.length > 1 || currentEnds.length > 1) {
|
|
154
|
+
throw new Error(`Existing file ${filePath} has duplicate standalone markers`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (currentStarts.length !== 1 || currentEnds.length !== 1) {
|
|
158
|
+
throw new Error(`Existing file ${filePath} has unbalanced standalone markers`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const currentStart = currentStarts[0];
|
|
162
|
+
const currentEnd = currentEnds[0];
|
|
163
|
+
if (currentEnd.start < currentStart.end) {
|
|
164
|
+
throw new Error(`Existing file ${filePath} has unbalanced standalone markers`);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const before = current.slice(0, currentStart.start);
|
|
168
|
+
const after = current.slice(currentEnd.end);
|
|
129
169
|
const next = before + blockContent.trim() + after;
|
|
130
170
|
if (next === current) return 'unchanged';
|
|
131
171
|
fs.writeFileSync(filePath, next, 'utf-8');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brunosps00/dev-workflow",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.1",
|
|
4
4
|
"description": "AI-driven development workflow commands for any project. Scaffolds a complete PRD-to-PR pipeline with multi-platform AI assistant support.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"dev-workflow": "./bin/dev-workflow.js"
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"repository": {
|
|
44
44
|
"type": "git",
|
|
45
|
-
"url": "https://github.com/
|
|
45
|
+
"url": "https://github.com/brunosps/dev-workflow"
|
|
46
46
|
},
|
|
47
47
|
"author": "Bruno Santos"
|
|
48
48
|
}
|
|
@@ -38,12 +38,13 @@ Use `/dw-autopilot "wish"` as the gateway for most feature work. The granular co
|
|
|
38
38
|
| `/dw-commit` | Atomic Conventional Commits for pending work. |
|
|
39
39
|
| `/dw-generate-pr [target]` | Push branch, draft PR body, open browser. |
|
|
40
40
|
|
|
41
|
-
## Tier 3 — Specialty (
|
|
41
|
+
## Tier 3 — Specialty (12)
|
|
42
42
|
|
|
43
43
|
| Command | What |
|
|
44
44
|
|---------|------|
|
|
45
45
|
| `/dw-analyze-project` | Scan the repo, write `.dw/rules/`, offer `.dw/constitution.md`, generate `.dw/rules/concerns.md`, and synthesize frontend `DESIGN.md` when tokens exist. |
|
|
46
46
|
| `/dw-redesign-ui "target"` | Audit, propose 2-3 design directions, ship. Enforces UI grounding + WCAG; can run the deterministic impeccable slop detector. |
|
|
47
|
+
| `/dw-open-design --brief <file>` | Generate, iterate, or `--refactor` headless Open Design HTML prototypes via `od`; refines briefs first, can capture current screenshots with `--url --viewports` or `--screenshot`, then verifies file + visual gate. |
|
|
47
48
|
| `/dw-refactor "target"` | Audit code-health and tech debt with Fowler smells, deep-modules analysis, and behavior-preserving test gates. |
|
|
48
49
|
| `/dw-functional-doc` | Map screens + flows into a functional doc validated with Playwright; uses WSL-resilient browser resolution. |
|
|
49
50
|
| `/dw-context-budget` | Audit context overhead from commands, skills, agents, instructions, and MCPs. |
|
|
@@ -112,6 +113,7 @@ Match the keyword and suggest:
|
|
|
112
113
|
| `research`, `compare`, `state of the art` | `/dw-brainstorm --research` |
|
|
113
114
|
| `refactor`, `smell`, `code health`, `tech debt` | `/dw-refactor` |
|
|
114
115
|
| `ui`, `design`, `redesign` | `/dw-redesign-ui` |
|
|
116
|
+
| `open design`, `prototype`, `prototipo`, `od cli`, `html prototype` | `/dw-open-design --brief <file>`, `/dw-open-design --brief-glob "<glob>" --agent claude`, or `/dw-open-design --refactor --url <url> --viewports "1440x900,375x812"` |
|
|
115
117
|
| `intel`, `where is`, `what uses` | `/dw-intel` (or `--build` to (re)create the index) |
|
|
116
118
|
| `context`, `tokens`, `slow agent` | `/dw-context-budget` |
|
|
117
119
|
| `harness`, `install`, `wrappers`, `agents` | `/dw-harness-audit` |
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
<system_instructions>
|
|
2
|
-
You are a workspace bootstrap lead for the dev-workflow ecosystem. Your job is to take an empty
|
|
2
|
+
You are a workspace bootstrap lead for the dev-workflow ecosystem. Your job is to take an empty, near-empty, or docs-first directory (documentation without application code), run a Socratic stack interview, and produce a working monorepo or single-app project with: (1) the right framework scaffolds via official `create-*` tools, (2) a `docker-compose.dev.yml` covering every selected dev dependency (db, cache, queue, email, storage, search, observability, proxy), (3) `.env.example`, scripts, `.gitignore`, `.dockerignore`, GitHub Action, README, and (4) a seeded `.dw/rules/index.md`.
|
|
3
3
|
|
|
4
4
|
<critical>This command MUST run AFTER `npx dev-workflow init` has populated `.dw/`. If `.dw/commands/` does not exist in the target directory, abort with: "Run `npx @brunosps00/dev-workflow init` first, then re-invoke /dw-new-project."</critical>
|
|
5
5
|
<critical>NEVER touch files outside the new project's directory. The interview captures `{{TARGET_DIR}}`; all writes are scoped under it.</critical>
|
|
6
6
|
<critical>Phase 3 (execution) runs ONLY after the user explicitly approves the plan presented in Phase 2. No flag bypass.</critical>
|
|
7
|
-
<critical>
|
|
7
|
+
<critical>Mailpit is the DEFAULT for email-in-dev. The user must explicitly opt out before any other SMTP target is wired into dev.</critical>
|
|
8
8
|
|
|
9
9
|
## When to Use
|
|
10
10
|
|
|
11
|
-
- Starting a new project from an empty directory and you want the dev-workflow conventions, containerized infra, and CI scaffolding from day one
|
|
11
|
+
- Starting a new project from an empty or docs-first directory and you want the dev-workflow conventions, containerized infra, and CI scaffolding from day one
|
|
12
12
|
- Replacing manual `pnpm create next-app && create vite ...` ceremony with a guided interview that captures the full dev environment
|
|
13
13
|
- Spinning up a learning sandbox where you want a realistic stack (db + cache + email + observability) without 30 minutes of YAML
|
|
14
|
-
- NOT for adding services to
|
|
14
|
+
- NOT for adding services to a project that already has manifests or application code — use `/dw-dockerize --audit`
|
|
15
15
|
- NOT for adding a new app inside an existing monorepo — that needs a different command (planned for a future release)
|
|
16
16
|
- NOT a replacement for `/dw-plan prd` — this generates the workspace, not the product spec
|
|
17
17
|
|
|
@@ -52,7 +52,7 @@ Execute phases in order. Phase 3 runs ONLY after user approval at the end of Pha
|
|
|
52
52
|
1. Verify `.dw/commands/` exists in `{{TARGET_DIR}}`. If not, abort with the message above.
|
|
53
53
|
2. Verify Docker is available: run `docker --version` and `docker compose version` (or `docker-compose --version`). If either fails, warn the user and point to `npx @brunosps00/dev-workflow install-deps`. Do NOT abort — the user may want a `--dry-run` plan even without Docker.
|
|
54
54
|
3. Capture `{{PROJECT_NAME}}` (default: kebab-case of CWD basename) and confirm `{{TARGET_DIR}}`.
|
|
55
|
-
4.
|
|
55
|
+
4. Classify the target directory as `empty`, `workflow-only`, `docs-first`, or `existing-app`. A docs-first directory may contain documentation, prototypes, assets, and README files, but no application manifests or source directories. List every path that will be preserved and ask for confirmation. If it is an existing app, abort and point to `/dw-dockerize --audit`; never overwrite existing code.
|
|
56
56
|
|
|
57
57
|
Emit a VERIFICATION REPORT for Phase 0 (Docker version captured, target dir state).
|
|
58
58
|
|
|
@@ -68,21 +68,21 @@ Use `AskUserQuestion` when available; otherwise plain numbered prompts. Ask in *
|
|
|
68
68
|
2. **Language(s)**: TypeScript/JavaScript, Python, C#, Rust (per app)
|
|
69
69
|
3. **Framework per layer** (curated list — refuse anything outside):
|
|
70
70
|
- **Frontend**: Next.js (app router), Vite + React (TS template)
|
|
71
|
-
- **Backend**: FastAPI (Python), ASP.NET Core minimal API (C#), Axum (Rust)
|
|
72
|
-
- **Fullstack
|
|
71
|
+
- **Backend**: NestJS or Fastify (Node TS), FastAPI (Python), ASP.NET Core minimal API (C#), Axum (Rust)
|
|
72
|
+
- **Fullstack**: T3 stack (single bundle), Next.js + NestJS, or Next.js + FastAPI (separate apps in a monorepo)
|
|
73
73
|
4. **Package manager** (NO default — ask explicitly):
|
|
74
74
|
- For Node: npm / pnpm / yarn
|
|
75
75
|
- For Python: poetry / uv / pip + venv
|
|
76
76
|
- For .NET: dotnet (built-in)
|
|
77
77
|
- For Rust: cargo (built-in)
|
|
78
|
-
5. **If fullstack** —
|
|
78
|
+
5. **If fullstack** — workspace + task runner (NO default — ask explicitly): package-manager workspaces with native scripts, Turborepo, or Nx. Record combinations such as `pnpm workspaces + Turborepo`.
|
|
79
79
|
|
|
80
80
|
#### Layer B — Infra (only ask what fits the shape)
|
|
81
81
|
|
|
82
|
-
6. **Database**: Postgres / MySQL / SQLite (file, no service) / MongoDB (out of scope for compose recipes — note and skip if chosen) / none
|
|
82
|
+
6. **Database**: Postgres / Postgres + pgvector / MySQL / SQLite (file, no service) / MongoDB (out of scope for compose recipes — note and skip if chosen) / none. For Postgres + pgvector, select an explicit supported recipe/version.
|
|
83
83
|
7. **Cache**: Redis / Memcached / none
|
|
84
|
-
8. **Queue / message broker**: BullMQ (Node
|
|
85
|
-
9. **Email — dev capture** (default: **
|
|
84
|
+
8. **Queue / message broker**: pg-boss (Node + Postgres, no extra service), BullMQ (Node + Redis), Celery (Python only), RabbitMQ (any), LocalStack SQS (any), none. If chosen, also ask whether the project will have async workers.
|
|
85
|
+
9. **Email — dev capture** (default: **Mailpit**, ask only if user wants to override): Mailpit / MailHog / smtp4dev / skip
|
|
86
86
|
10. **Email — prod target** (only ask if user wants email at all): SMTP relay / SendGrid / Resend / Postmark / SES / skip
|
|
87
87
|
11. **Object storage**: S3 (real, no service in compose) / MinIO (dev) / GCS (no service) / none
|
|
88
88
|
12. **Search**: Meilisearch / Typesense / Elasticsearch / none
|
|
@@ -93,7 +93,8 @@ Use `AskUserQuestion` when available; otherwise plain numbered prompts. Ask in *
|
|
|
93
93
|
#### Layer C — Tooling
|
|
94
94
|
|
|
95
95
|
16. **Auth** (only ask if applicable to chosen stack):
|
|
96
|
-
- Next.js:
|
|
96
|
+
- Next.js: Auth.js / generic OIDC / Clerk / custom JWT / none
|
|
97
|
+
- NestJS: generic OIDC / Passport JWT / custom auth / none
|
|
97
98
|
- FastAPI: fastapi-users / authlib / custom JWT / none
|
|
98
99
|
- ASP.NET: built-in Identity / IdentityServer / custom JWT / none
|
|
99
100
|
- Axum: tower-cookies + jsonwebtoken / custom / none
|
|
@@ -102,7 +103,8 @@ Use `AskUserQuestion` when available; otherwise plain numbered prompts. Ask in *
|
|
|
102
103
|
- Python: Ruff + Black / Ruff only
|
|
103
104
|
- C#: dotnet format
|
|
104
105
|
- Rust: rustfmt + clippy (default)
|
|
105
|
-
18. **
|
|
106
|
+
18. **Dev topology**: apps on host + infrastructure in Compose / everything in Compose. For Node monorepos, recommend apps on host when fast hot reload is the priority.
|
|
107
|
+
19. **CI**: GitHub Actions (always seed; user can opt out)
|
|
106
108
|
|
|
107
109
|
Save all answers in memory for Phase 2.
|
|
108
110
|
|
|
@@ -133,6 +135,7 @@ Run in this order. Each step emits its own mini-VERIFICATION block.
|
|
|
133
135
|
| Next.js | `pnpm create next-app@latest <dir> --ts --tailwind --eslint --app --import-alias '@/*' --use-pnpm --no-git` |
|
|
134
136
|
| Vite + React | `pnpm create vite@latest <dir> --template react-ts` |
|
|
135
137
|
| T3 | `pnpm dlx create-t3-app@latest <dir> --noGit --CI --tailwind --trpc --prisma --nextAuth --appRouter` |
|
|
138
|
+
| NestJS | `pnpm dlx @nestjs/cli@latest new <dir> --package-manager pnpm --skip-git --strict` |
|
|
136
139
|
| Fastify | `pnpm create fastify@latest <dir>` then trim interactive prompts; if no non-interactive flag works, generate the structure inline (`src/server.ts` + `src/routes/` + `package.json`) |
|
|
137
140
|
| FastAPI | NO official `create-*`. Generate inline: `pyproject.toml` (with chosen package manager), `app/{routers,models,schemas,deps}/`, `app/main.py`, `tests/` skeleton |
|
|
138
141
|
| ASP.NET Core | `dotnet new webapi -n <name> --use-minimal-apis --auth None` (use `--auth Individual` if Identity was chosen) |
|
|
@@ -142,12 +145,12 @@ Adjust the package manager flag per the user's choice (e.g., `--use-npm`, `--use
|
|
|
142
145
|
|
|
143
146
|
For **fullstack-T3**: that's it for app code (T3 ships everything in one tree).
|
|
144
147
|
|
|
145
|
-
For **fullstack-NextJS+FastAPI**:
|
|
148
|
+
For **fullstack-NextJS+NestJS** or **fullstack-NextJS+FastAPI**: scaffold directly into `apps/web/` and `apps/api/` without moving or deleting pre-existing documentation.
|
|
146
149
|
|
|
147
150
|
#### 3.2 Compose monorepo (fullstack only)
|
|
148
151
|
|
|
149
152
|
If fullstack:
|
|
150
|
-
1.
|
|
153
|
+
1. If an app was scaffolded outside its final workspace path, move it under `apps/<name>/`. Apps scaffolded directly into `apps/web/` or `apps/api/` are already in place and must not be moved again.
|
|
151
154
|
2. Create `pnpm-workspace.yaml` (or equivalent), root `package.json` with workspace scripts, root `tsconfig.base.json` if shared TS config.
|
|
152
155
|
3. If user picked Turborepo: add `turbo.json` with `dev`, `build`, `lint`, `test` pipelines.
|
|
153
156
|
4. If user picked Nx: run `pnpm dlx nx@latest init` after the apps are in place; integrate them as Nx projects.
|
|
@@ -159,7 +162,7 @@ If fullstack:
|
|
|
159
162
|
- Concatenate selected service blocks under `services:`.
|
|
160
163
|
- Aggregate named volumes under `volumes:`.
|
|
161
164
|
- Resolve port collisions if any.
|
|
162
|
-
-
|
|
165
|
+
- For `everything in Compose`, add the app service(s) at the end (build context = `apps/<name>` or root, Dockerfile.dev, env_file, volumes, depends_on with `condition: service_healthy`). For `apps on host`, compose infrastructure only and use `localhost` endpoints in app-facing environment variables.
|
|
163
166
|
3. Add a header comment: `# Generated by /dw-new-project on YYYY-MM-DD`.
|
|
164
167
|
|
|
165
168
|
#### 3.4 Generate `.env.example`
|
|
@@ -214,7 +217,7 @@ Minimal scaffold:
|
|
|
214
217
|
| Database | <db or n/a> |
|
|
215
218
|
| Cache | <cache or n/a> |
|
|
216
219
|
| Queue | <queue or n/a> |
|
|
217
|
-
| Email (dev) | <mailhog|
|
|
220
|
+
| Email (dev) | <mailpit|mailhog|smtp4dev|none> |
|
|
218
221
|
| Search | <search or n/a> |
|
|
219
222
|
| Observability | <observability or n/a> |
|
|
220
223
|
| Reverse proxy | <traefik|none> |
|
|
@@ -230,7 +233,7 @@ Minimal scaffold:
|
|
|
230
233
|
## Conventions
|
|
231
234
|
|
|
232
235
|
- See `.dw/rules/<module>.md` after `/dw-analyze-project` runs.
|
|
233
|
-
- Email-in-dev uses
|
|
236
|
+
- Email-in-dev uses Mailpit by default; the app NEVER sends real mail in dev.
|
|
234
237
|
- All env vars live in `.env` (gitignored); `.env.example` is the template.
|
|
235
238
|
```
|
|
236
239
|
|
|
@@ -273,7 +276,7 @@ date: YYYY-MM-DD
|
|
|
273
276
|
shape: <frontend|backend|fullstack>
|
|
274
277
|
languages: [typescript, python, ...]
|
|
275
278
|
frameworks: { web: '...', api: '...' }
|
|
276
|
-
services: [postgres,
|
|
279
|
+
services: [postgres-pgvector, mailpit, minio, ...]
|
|
277
280
|
package_manager: <pnpm|npm|yarn|poetry|uv|cargo|dotnet>
|
|
278
281
|
monorepo: <pnpm-workspaces|turborepo|nx|none>
|
|
279
282
|
---
|
|
@@ -302,7 +305,7 @@ monorepo: <pnpm-workspaces|turborepo|nx|none>
|
|
|
302
305
|
1. `cp .env.example .env` and review credentials.
|
|
303
306
|
2. `pnpm install` (or your chosen package manager).
|
|
304
307
|
3. `pnpm dev:up` to bring up all services. Wait for healthchecks.
|
|
305
|
-
4. Open
|
|
308
|
+
4. Open Mailpit UI at http://localhost:8025 to confirm email capture is wired.
|
|
306
309
|
5. `/dw-plan prd` to draft the first feature.
|
|
307
310
|
6. After your first substantial commit, run `/dw-analyze-project` to enrich `.dw/rules/`.
|
|
308
311
|
```
|
|
@@ -332,7 +335,7 @@ monorepo: <pnpm-workspaces|turborepo|nx|none>
|
|
|
332
335
|
- User picks MongoDB → note "MongoDB recipe not bundled in v0.8.0; we'll add app dependencies but you'll need to wire the service manually". Continue.
|
|
333
336
|
- User picks Caddy → same: note as not in bundled recipes; continue without compose service.
|
|
334
337
|
- Port already bound on host → suggest the override env var and continue; do not pick a different port silently.
|
|
335
|
-
-
|
|
338
|
+
- Docs-first working tree → preserve every listed path and write only to approved new paths. Existing app → abort and point to `/dw-dockerize --audit`.
|
|
336
339
|
|
|
337
340
|
## Integration With Other dw-* Commands
|
|
338
341
|
|