@brunosps00/dev-workflow 1.7.0 → 2.0.0

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.
Files changed (67) hide show
  1. package/.claude-plugin/plugin.json +2 -2
  2. package/CHANGELOG.md +201 -0
  3. package/README.md +2 -2
  4. package/lib/agents.js +6 -1
  5. package/lib/init.js +14 -0
  6. package/lib/install-deps.js +15 -0
  7. package/lib/install-open-design.js +39 -0
  8. package/lib/install-state.js +8 -4
  9. package/lib/utils.js +46 -6
  10. package/package.json +3 -2
  11. package/scaffold/agents/core/dw-build-fixer.md +0 -1
  12. package/scaffold/agents/core/dw-code-explorer.md +0 -1
  13. package/scaffold/agents/core/dw-code-reviewer.md +0 -1
  14. package/scaffold/agents/core/dw-planner.md +0 -1
  15. package/scaffold/agents/core/dw-test-author.md +0 -1
  16. package/scaffold/agents/csharp/dw-csharp-reviewer.md +0 -1
  17. package/scaffold/agents/csharp/dw-dotnet-build-fixer.md +0 -1
  18. package/scaffold/agents/frontend/dw-qa-runner.md +0 -1
  19. package/scaffold/agents/python/dw-python-build-fixer.md +0 -1
  20. package/scaffold/agents/python/dw-python-reviewer.md +0 -1
  21. package/scaffold/agents/rust/dw-rust-build-fixer.md +0 -1
  22. package/scaffold/agents/rust/dw-rust-reviewer.md +0 -1
  23. package/scaffold/agents/security/dw-security-reviewer.md +0 -1
  24. package/scaffold/agents/security/dw-silent-failure-hunter.md +0 -1
  25. package/scaffold/agents/typescript/dw-typescript-build-fixer.md +0 -1
  26. package/scaffold/agents/typescript/dw-typescript-reviewer.md +0 -1
  27. package/scaffold/config/routing.json +60 -0
  28. package/scaffold/en/commands/dw-claude-run.md +5 -1
  29. package/scaffold/en/commands/dw-codex-run.md +5 -1
  30. package/scaffold/en/commands/dw-dockerize.md +14 -7
  31. package/scaffold/en/commands/dw-generate-pr.md +13 -0
  32. package/scaffold/en/commands/dw-new-project.md +34 -30
  33. package/scaffold/en/commands/dw-run.md +1 -1
  34. package/scaffold/en/templates/project-onepager.md +16 -14
  35. package/scaffold/en/templates/task-template.md +1 -1
  36. package/scaffold/en/templates/tasks-template.md +7 -5
  37. package/scaffold/pt-br/commands/dw-claude-run.md +5 -1
  38. package/scaffold/pt-br/commands/dw-codex-run.md +5 -1
  39. package/scaffold/pt-br/commands/dw-dockerize.md +14 -7
  40. package/scaffold/pt-br/commands/dw-generate-pr.md +13 -0
  41. package/scaffold/pt-br/commands/dw-new-project.md +34 -30
  42. package/scaffold/pt-br/commands/dw-plan.md +1 -1
  43. package/scaffold/pt-br/commands/dw-run.md +1 -1
  44. package/scaffold/pt-br/templates/project-onepager.md +16 -14
  45. package/scaffold/pt-br/templates/task-template.md +1 -1
  46. package/scaffold/pt-br/templates/tasks-template.md +7 -5
  47. package/scaffold/skills/docker-compose-recipes/SKILL.md +4 -5
  48. package/scaffold/skills/docker-compose-recipes/references/compose-composition.md +6 -6
  49. package/scaffold/skills/docker-compose-recipes/references/env-conventions.md +18 -9
  50. package/scaffold/skills/docker-compose-recipes/references/healthcheck-patterns.md +4 -5
  51. package/scaffold/skills/docker-compose-recipes/references/prod-vs-dev.md +7 -2
  52. package/scaffold/skills/docker-compose-recipes/services/elasticsearch.yml +3 -3
  53. package/scaffold/skills/docker-compose-recipes/services/mailpit.yml +4 -4
  54. package/scaffold/skills/docker-compose-recipes/services/meilisearch.yml +2 -2
  55. package/scaffold/skills/docker-compose-recipes/services/minio.yml +8 -4
  56. package/scaffold/skills/docker-compose-recipes/services/mysql.yml +4 -4
  57. package/scaffold/skills/docker-compose-recipes/services/postgres.yml +2 -2
  58. package/scaffold/skills/docker-compose-recipes/services/rabbitmq.yml +3 -3
  59. package/scaffold/skills/docker-compose-recipes/services/typesense.yml +3 -3
  60. package/scaffold/skills/dw-cli-run/SKILL.md +42 -38
  61. package/scaffold/skills/dw-cli-run/references/dispatch-tuning.md +85 -0
  62. package/scaffold/skills/dw-execute-phase/SKILL.md +4 -2
  63. package/scaffold/skills/dw-execute-phase/agents/executor.md +4 -2
  64. package/scaffold/skills/dw-execute-phase/agents/plan-checker.md +9 -7
  65. package/scaffold/skills/dw-execute-phase/references/atomic-commits.md +12 -8
  66. package/scaffold/skills/dw-execute-phase/references/plan-verification.md +6 -4
  67. package/scaffold/skills/docker-compose-recipes/services/mailhog.yml +0 -23
@@ -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.7.0",
4
+ "version": "2.0.0",
5
5
  "author": {
6
6
  "name": "Bruno Santos"
7
7
  },
8
- "homepage": "https://github.com/brunosantos/dev-workflow",
8
+ "homepage": "https://github.com/brunosps/dev-workflow",
9
9
  "license": "MIT",
10
10
  "keywords": [
11
11
  "ai",
package/CHANGELOG.md ADDED
@@ -0,0 +1,201 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this
6
+ project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ > Entries for 2.0.0 are written from the change set itself. Entries before it were
9
+ > reconstructed from commit history in August 2026 — this file did not exist while
10
+ > those versions were released, so they are summaries of what shipped, not
11
+ > contemporaneous release notes. `git log` remains the authoritative record.
12
+
13
+ ## [2.0.0] — 2026-08-02
14
+
15
+ First release after a full security audit of the compose recipes. Two options were
16
+ removed rather than shipped with vulnerabilities the project could not close.
17
+
18
+ Versions 1.8.0, 1.9.0 and 1.10.0 exist as commits but were never published to npm;
19
+ their contents are folded in here. The last published version was 1.7.1.
20
+
21
+ ### Removed
22
+
23
+ - **`Postgres + pgvector` is no longer offered by `/dw-new-project`**, and
24
+ `services/postgres-pgvector.yml` is deleted. After repinning to the newest
25
+ available base the image still carried 18 CRITICAL, none of them actionable: 15
26
+ were Go `stdlib` inside a vendored binary (only a maintainer rebuild clears
27
+ those) and the rest have no upstream fix at all (`perl` 5.40, `libxml2`
28
+ `CVE-2026-6653`). Shipping a recipe whose CVEs the project cannot close was not
29
+ a defensible default for an optional capability. **Existing projects are
30
+ unaffected** — `/dw-dockerize` keeps whatever image the project already uses and
31
+ tells the user it needs its own review, rather than substituting one.
32
+ - **MailHog is no longer offered as a dev email-capture option**, and
33
+ `services/mailhog.yml` is deleted. It reported 109 CRITICAL / 1250 HIGH with
34
+ 1359 of those carrying a published fix — the profile of an image not rebuilt
35
+ since its upstream went quiet in 2020. Mailpit, already the documented default,
36
+ scans 0 CRITICAL / 0 HIGH for the same job.
37
+
38
+ ### Security
39
+
40
+ - **Every credential in every compose recipe now uses `${VAR:?message}`** instead
41
+ of `${VAR:-default}`, so Compose refuses to start rather than falling back to a
42
+ value published in this repository. Applied to `postgres`, `mysql`,
43
+ `elasticsearch`, `meilisearch`, `minio`, `rabbitmq`, and `typesense`.
44
+ - **Every authenticated service publishes on `127.0.0.1` only.** Docker's
45
+ `"HOST:CONTAINER"` short form binds every host interface, which turns a local
46
+ dev credential into a network-reachable one.
47
+ - **`typesense` was pinned to `0.27.1`, a tag that does not exist** — the recipe
48
+ could never have worked (`MANIFEST_UNKNOWN`). Repinned to `30.2`. It also passed
49
+ its API key as a `command:` flag with a working default, which the first version
50
+ of the credential test missed because it only read `environment:` keys.
51
+ - **`minio` moved off `minio/minio:latest`** to `RELEASE.2025-09-07T16-13-09Z`.
52
+ This does not reduce CVEs — MinIO stopped publishing to Docker Hub after that
53
+ release, so `latest` resolves to the same image — but it makes a scan result
54
+ describe what the next user runs rather than what was pulled that day.
55
+ - **No recipe uses an image digest or a floating tag.** A digest freezes the base
56
+ image and accrues CVEs with no upgrade path; the previously pinned pgvector
57
+ digest went from 1 to 22 CRITICAL in three weeks without the image changing at
58
+ all, because only the vulnerability database moved.
59
+
60
+ ### Changed
61
+
62
+ - **CLI dispatch is standardized on spawning a CLI with an explicit model and
63
+ effort**, for both Claude and Codex. Codex has no subagent primitive, so spawn
64
+ was already the only option there; standardizing removes the asymmetry. Every
65
+ dispatch now declares **WRITE** or **READ-ONLY** — the hard worktree rule binds
66
+ to WRITE only, since a read-only dispatch cannot edit, which is the entire
67
+ reason the rule exists.
68
+ - **In-session subagents inherit the session model.** Every generated Claude
69
+ subagent previously carried `model: sonnet`, silently overriding an Opus or
70
+ Fable session for planning and review work with no way to see it from the
71
+ harness. Sizing now belongs to the dispatch, not to the agent definition.
72
+ - **Requirement IDs are language-neutral in the shared skills.** `dw-execute-phase`
73
+ is shared by English and Portuguese projects but hard-coded the Portuguese token
74
+ `RF-XX`, so its `plan-checker` searched `tasks.md` for a string English projects
75
+ never generate (`FR-1.1`). Requirement-coverage verification could not match.
76
+ - `tasks.md` gained a `Commit` column, giving `/dw-run` step 6 ("mark Done with
77
+ the commit SHA") somewhere to write. It previously had no destination.
78
+ - `/dw-generate-pr` records the PR URL back into the PRD. Nothing on the `.dw/`
79
+ side previously recorded which PR delivered the work.
80
+
81
+ ### Added
82
+
83
+ - `.dw/config/routing.json` — maps a task's Conventional-Commit type to a tier,
84
+ and a tier to a concrete model and effort per brand. Seeded once and never
85
+ reconciled on update, because model availability differs per account.
86
+ - `references/dispatch-tuning.md` in `dw-cli-run`, which declared
87
+ `load_policy: lazy-references` but shipped none. The dual-evaluation protocol,
88
+ escalation ladder, and cold-start tuning moved there.
89
+ - Per-dispatch MCP kill switches: `--strict-mcp-config` (Claude),
90
+ `-c mcp_servers='{}'` (Codex). A spawned CLI boots every configured MCP server
91
+ and shares no prompt cache, which can outweigh a small task.
92
+ - `test/compose-recipe-safety.test.js` and `test/dispatch-routing.test.js`.
93
+
94
+ ### Fixed
95
+
96
+ - `.claude-plugin/plugin.json` was stuck at `1.7.0` while `package.json` said
97
+ `1.7.1`, leaving `npm run validate` failing on `main`.
98
+ - Managed-file manifest deduplication and pgvector dev defaults — both committed
99
+ on 2026-07-15 but never merged or published until now.
100
+
101
+ ### Notes
102
+
103
+ The regression guards added here deliberately cover **classes**, not files. The
104
+ July 2026 audit raised default credentials against one recipe; that fix was scoped
105
+ to the file in the diff, and the August audit found the same defect in six
106
+ siblings. `compose-recipe-safety.test.js` now fails on any credential default, any
107
+ non-loopback published port on an authenticated service, any digest pin, and any
108
+ floating tag — across every recipe.
109
+
110
+ A full image sweep covered all 16 referenced images, up from 2. Results and the
111
+ residual advisories live in `.dw/secure-audit/` (local, gitignored).
112
+
113
+ ## [1.7.1] — 2026-07-24
114
+
115
+ ### Fixed
116
+ - `/dw-open-design` command, wrappers, and scripts are synced into the project by
117
+ `install-deps`.
118
+
119
+ ## [1.7.0] — 2026-07-15
120
+
121
+ ### Added
122
+ - `/dw-open-design` command, with a refactor screenshot mode and a requirement
123
+ that briefs be refined before runs.
124
+ - Docs-first NestJS bootstrap in `/dw-new-project` (Next.js + NestJS, pnpm
125
+ workspaces + Turborepo, pg-boss, Mailpit, dev topology).
126
+
127
+ ### Fixed
128
+ - Managed instruction blocks stay idempotent across updates.
129
+
130
+ ## [1.6.0] — 2026-07-15
131
+
132
+ Release commit exists; the change set is not cleanly separable from 1.7.0 in the
133
+ history (a source restore commit sits between them). See `git log 28ccc36..af6b157`.
134
+
135
+ ## [1.5.0] — 2026-07-02
136
+
137
+ ### Added
138
+ - ECC-inspired upgrades: additional skills, runtime cost tracking, a curated rules
139
+ library, and the instincts memory layer.
140
+
141
+ ## [1.4.0] — 2026-06-29
142
+
143
+ ### Added
144
+ - Skills ecosystem comparison documentation and the README adoptions that followed.
145
+
146
+ ## [1.3.0] — 2026-06-23
147
+
148
+ ### Added
149
+ - À-la-carte plugin distribution plus a repo `validate` gate.
150
+ - `git-guardrails` and statusline enforcement hooks.
151
+ - Per-skill invocation control via `disable-model-invocation`.
152
+ - `dw-minimalism` (YAGNI decision ladder) and `dw-cli-run` (Claude/Codex/Copilot
153
+ runner protocol) skills.
154
+
155
+ ## [1.2.1] — 2026-05-27
156
+
157
+ ### Fixed
158
+ - Context-budget wrapper name collision.
159
+
160
+ ## [1.2.0] — 2026-05-27
161
+
162
+ ### Added
163
+ - Structured return contracts across skills.
164
+ - `/dw-opportunities` and `/dw-refactor` workflows.
165
+
166
+ ## [1.1.0] – [1.1.4] — 2026-05-25
167
+
168
+ ### Added
169
+ - WSL-resilient browser layer, Security Gate, and the `impeccable` absorption.
170
+ - Portable `/dw-goal` autopilot flow and the subagent handoff workflow.
171
+ - Post-update action guidance.
172
+
173
+ ### Fixed
174
+ - Playwright browser selection prefers a real browser with CDP fallback; the
175
+ reverse CDP relay ships prebuilt and runs at user level.
176
+
177
+ ## [1.0.0] – [1.0.6] — 2026-05-13
178
+
179
+ Surface consolidation release: 15 obsolete commands deleted and the remainder
180
+ merged into a 20-command surface (later 38 with the tiers added).
181
+
182
+ ### Changed
183
+ - `create-prd` + `create-techspec` + `create-tasks` → `/dw-plan`
184
+ - `run-task` + `run-plan` → `/dw-run`
185
+ - `review-implementation` + `code-review` → `/dw-review`
186
+ - `run-qa` + `fix-qa` → `/dw-qa`
187
+ - `security-check` + `deps-audit` → `/dw-secure-audit`
188
+ - `map-codebase` folded into `/dw-intel --build`
189
+
190
+ ### Added
191
+ - Removed-commands manifest with old-to-new mapping for migration.
192
+ - Tiered `/dw-help` output with an `--advanced` flag.
193
+ - Opt-in AWS and Azure skill installers with their respective MCP servers.
194
+
195
+ ## [0.1.0] – [0.15.0] — 2026-04-06 to 2026-05-13
196
+
197
+ Pre-1.0 development. The CLI, the scaffold layout, the command set, and the
198
+ multi-platform wrapper generation took shape across 35 releases in five weeks.
199
+ See `git log 6cd61cb..d6953f4`.
200
+
201
+ [2.0.0]: https://github.com/brunosps/dev-workflow/releases/tag/v2.0.0
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.7.0 ships **38 commands** organized into four tiers. Most users only invoke Tier 1 + Tier 2.
45
+ dev-workflow v2.0.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, composes docker-compose for dev, seeds `.env`, scripts, CI, `.dw/rules/`. |
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, 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/agents.js CHANGED
@@ -176,7 +176,12 @@ function renderClaudeAgent(agent, source) {
176
176
  `name: ${agent.name}`,
177
177
  `description: "${yamlString(agent.description)}"`,
178
178
  `tools: ${tools.join(', ')}`,
179
- `model: ${source.metadata.model || 'sonnet'}`,
179
+ // `inherit` is Claude Code's documented default: the subagent runs on the
180
+ // session's model. Sizing happens at dispatch (the CLI runners pass an
181
+ // explicit --model/--effort per .dw/config/routing.json), so pinning a tier
182
+ // here would silently override the owner's session choice. A scaffold source
183
+ // agent may still opt out by declaring its own `model:`.
184
+ `model: ${source.metadata.model || 'inherit'}`,
180
185
  `permissionMode: ${agent.tool_policy === 'read-only' ? 'plan' : 'default'}`,
181
186
  `maxTurns: ${agent.max_turns}`,
182
187
  '---',
package/lib/init.js CHANGED
@@ -194,6 +194,20 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
194
194
  touchedFiles.push(stackMapDest);
195
195
  }
196
196
 
197
+ // 3.95. Seed .dw/config/routing.json (dispatch tier/model/effort per brand for
198
+ // the CLI runners). Seeded once and NEVER reconciled: model availability differs
199
+ // per account, so the owner's edits must survive updates.
200
+ const routingSrc = path.join(SCAFFOLD_DIR, 'config', 'routing.json');
201
+ if (require('fs').existsSync(routingSrc)) {
202
+ const cfgDir = path.join(projectRoot, '.dw', 'config');
203
+ ensureDir(cfgDir);
204
+ const routingDest = path.join(cfgDir, 'routing.json');
205
+ const rtStatus = writeFile(routingDest, require('fs').readFileSync(routingSrc, 'utf-8'), false);
206
+ if (rtStatus === 'created') totalCreated++;
207
+ else totalSkipped++;
208
+ touchedFiles.push(routingDest);
209
+ }
210
+
197
211
  // 4. Create .dw/rules/ with README
198
212
  if (!isUpdate) {
199
213
  console.log(' Rules:');
@@ -90,6 +90,7 @@ function run() {
90
90
  check: openDesign.check,
91
91
  install: openDesign.install,
92
92
  instructions: openDesign.instructions(),
93
+ sync: openDesign.syncProjectAssets,
93
94
  },
94
95
  ];
95
96
 
@@ -122,6 +123,7 @@ function run() {
122
123
  try {
123
124
  if (typeof dep.check === 'function' && dep.check()) {
124
125
  console.log(' \x1b[32m✓ already installed\x1b[0m');
126
+ runDepSync(dep);
125
127
  installed++;
126
128
  continue;
127
129
  }
@@ -131,6 +133,7 @@ function run() {
131
133
  execSync(dep.install, { stdio: 'pipe', timeout: 300000 });
132
134
  }
133
135
  console.log(' \x1b[32m✓\x1b[0m');
136
+ runDepSync(dep);
134
137
  installed++;
135
138
  } catch (err) {
136
139
  console.log(' \x1b[31m✗\x1b[0m');
@@ -202,3 +205,15 @@ function printBrowserGuidance() {
202
205
  }
203
206
 
204
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
+ }
@@ -2,6 +2,12 @@ const fs = require('fs');
2
2
  const os = require('os');
3
3
  const path = require('path');
4
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');
5
11
 
6
12
  const OPEN_DESIGN_REPO = 'https://github.com/nexu-io/open-design';
7
13
  const OPEN_DESIGN_REF = '1cb7eae';
@@ -35,6 +41,38 @@ function install() {
35
41
  }
36
42
  }
37
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
+
38
76
  function instructions() {
39
77
  return [
40
78
  'Open Design CLI is provisioned by dev-workflow into a managed checkout:',
@@ -80,6 +118,7 @@ function isAtLeast(actual, minimum) {
80
118
  }
81
119
 
82
120
  module.exports = {
121
+ syncProjectAssets,
83
122
  OPEN_DESIGN_REF,
84
123
  OPEN_DESIGN_VENDOR_DIR,
85
124
  check,
@@ -42,10 +42,14 @@ function writeInstallState(projectRoot, state) {
42
42
  }
43
43
 
44
44
  function listManagedFiles(projectRoot, relativeFiles) {
45
- return relativeFiles
46
- .filter(Boolean)
47
- .map((filePath) => path.relative(projectRoot, filePath))
48
- .sort();
45
+ return [
46
+ ...new Set(
47
+ relativeFiles
48
+ .filter(Boolean)
49
+ .map((filePath) => path.relative(projectRoot, filePath))
50
+ .sort()
51
+ ),
52
+ ];
49
53
  }
50
54
 
51
55
  module.exports = {
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
- if (!blockContent.includes(startMarker) || !blockContent.includes(endMarker)) {
109
- throw new Error(`Block content for ${filePath} is missing its markers`);
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 hasMarkers = current.includes(startMarker) && current.includes(endMarker);
143
+ const currentStarts = findStandaloneMarkers(current, startMarker);
144
+ const currentEnds = findStandaloneMarkers(current, endMarker);
119
145
 
120
- if (!hasMarkers) {
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
- const before = current.slice(0, current.indexOf(startMarker));
128
- const after = current.slice(current.indexOf(endMarker) + endMarker.length);
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.7.0",
3
+ "version": "2.0.0",
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"
@@ -17,6 +17,7 @@
17
17
  "scaffold/",
18
18
  ".claude-plugin/",
19
19
  "README.md",
20
+ "CHANGELOG.md",
20
21
  "LICENSE"
21
22
  ],
22
23
  "keywords": [
@@ -42,7 +43,7 @@
42
43
  },
43
44
  "repository": {
44
45
  "type": "git",
45
- "url": "https://github.com/brunosantos/dev-workflow"
46
+ "url": "https://github.com/brunosps/dev-workflow"
46
47
  },
47
48
  "author": "Bruno Santos"
48
49
  }
@@ -2,7 +2,6 @@
2
2
  name: dw-build-fixer
3
3
  description: Fix build, typecheck, and lint failures with minimal diffs and no architectural rewrites.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-code-explorer
3
3
  description: Trace entry points, execution paths, dependencies, and local conventions before planning or fixing code.
4
4
  tools: Read, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-code-reviewer
3
3
  description: Review changed code for correctness, maintainability, tests, and concrete failure modes.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-planner
3
3
  description: Convert requirements into implementation slices, dependency order, risks, and verification criteria.
4
4
  tools: Read, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-test-author
3
3
  description: Add focused tests and regression coverage using project conventions and dw-testing-discipline.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-csharp-reviewer
3
3
  description: Review C#/.NET code for API, async, dependency injection, and data-access risks.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-dotnet-build-fixer
3
3
  description: Fix .NET build, test, restore, and analyzer failures with minimal changes.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-qa-runner
3
3
  description: Create and run UI/API QA scripts, evidence, and retest logs under QA folders.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-python-build-fixer
3
3
  description: Fix Python test, import, typing, and packaging failures with minimal changes.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-python-reviewer
3
3
  description: Review Python changes for correctness, typing, async behavior, and framework risks.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-rust-build-fixer
3
3
  description: Fix Rust cargo check/test/clippy failures with minimal changes.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-rust-reviewer
3
3
  description: Review Rust changes for ownership, error handling, concurrency, unsafe, and API design risks.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-security-reviewer
3
3
  description: Review auth, authorization, secrets, input handling, uploads, SQL, SSRF, XSS, and supply-chain risk.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-silent-failure-hunter
3
3
  description: Find swallowed errors, dangerous fallbacks, lost stack traces, and missing error propagation.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-typescript-build-fixer
3
3
  description: Fix TypeScript and JavaScript build/type errors with minimal changes.
4
4
  tools: Read, Write, Edit, Bash, Grep, Glob
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7
 
@@ -2,7 +2,6 @@
2
2
  name: dw-typescript-reviewer
3
3
  description: Review TypeScript and JavaScript changes for type safety, async correctness, and web or Node risks.
4
4
  tools: Read, Grep, Glob, Bash
5
- model: sonnet
6
5
  mode: subagent
7
6
  ---
8
7