@brunosps00/dev-workflow 1.3.0 → 1.5.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 (63) hide show
  1. package/.claude-plugin/marketplace.json +13 -0
  2. package/.claude-plugin/plugin.json +31 -0
  3. package/README.md +24 -4
  4. package/lib/build-plugin.js +79 -0
  5. package/lib/constants.js +19 -14
  6. package/lib/doctor.js +33 -0
  7. package/lib/hooks.js +179 -0
  8. package/lib/init.js +68 -0
  9. package/lib/skill-registry.js +13 -0
  10. package/lib/uninstall.js +22 -3
  11. package/lib/validate.js +62 -0
  12. package/lib/wrappers.js +1 -1
  13. package/package.json +6 -1
  14. package/scaffold/config/stack-mappings.json +40 -0
  15. package/scaffold/en/commands/dw-analyze-project.md +5 -1
  16. package/scaffold/en/commands/dw-context-budget.md +9 -0
  17. package/scaffold/en/commands/dw-harness-audit.md +3 -0
  18. package/scaffold/en/commands/dw-learn.md +41 -0
  19. package/scaffold/en/commands/dw-plan.md +3 -0
  20. package/scaffold/en/commands/dw-review.md +2 -0
  21. package/scaffold/en/commands/dw-run.md +2 -0
  22. package/scaffold/en/commands/dw-update.md +4 -1
  23. package/scaffold/pt-br/commands/dw-analyze-project.md +5 -1
  24. package/scaffold/pt-br/commands/dw-context-budget.md +9 -0
  25. package/scaffold/pt-br/commands/dw-harness-audit.md +3 -0
  26. package/scaffold/pt-br/commands/dw-learn.md +41 -0
  27. package/scaffold/pt-br/commands/dw-plan.md +3 -0
  28. package/scaffold/pt-br/commands/dw-review.md +2 -0
  29. package/scaffold/pt-br/commands/dw-run.md +2 -0
  30. package/scaffold/pt-br/commands/dw-update.md +4 -1
  31. package/scaffold/rules/README.md +22 -0
  32. package/scaffold/rules/common.md +29 -0
  33. package/scaffold/rules/csharp.md +18 -0
  34. package/scaffold/rules/python.md +18 -0
  35. package/scaffold/rules/react.md +18 -0
  36. package/scaffold/rules/rust.md +18 -0
  37. package/scaffold/rules/typescript.md +23 -0
  38. package/scaffold/rules-readme.md +8 -0
  39. package/scaffold/scripts/hooks/git-guardrails.mjs +79 -0
  40. package/scaffold/scripts/hooks/session-cost.mjs +126 -0
  41. package/scaffold/scripts/hooks/statusline.mjs +118 -0
  42. package/scaffold/scripts/lib/model-prices.json +12 -0
  43. package/scaffold/skill-registry.json +27 -0
  44. package/scaffold/skills/dw-cli-run/SKILL.md +3 -1
  45. package/scaffold/skills/dw-codebase-intel/SKILL.md +3 -1
  46. package/scaffold/skills/dw-debug-protocol/SKILL.md +4 -0
  47. package/scaffold/skills/dw-debug-protocol/references/agent-degradation.md +30 -0
  48. package/scaffold/skills/dw-execute-phase/SKILL.md +13 -0
  49. package/scaffold/skills/dw-git-discipline/SKILL.md +1 -0
  50. package/scaffold/skills/dw-llm-eval/SKILL.md +12 -0
  51. package/scaffold/skills/dw-memory/SKILL.md +12 -1
  52. package/scaffold/skills/dw-memory/references/instincts.md +45 -0
  53. package/scaffold/skills/dw-minimalism/SKILL.md +98 -0
  54. package/scaffold/skills/dw-review-rigor/SKILL.md +12 -0
  55. package/scaffold/skills/dw-review-rigor/references/false-positives.md +21 -0
  56. package/scaffold/skills/dw-review-rigor/references/self-eval-rubric.md +38 -0
  57. package/scaffold/skills/dw-silent-failure/SKILL.md +4 -2
  58. package/scaffold/skills/dw-simplification/SKILL.md +13 -0
  59. package/scaffold/skills/dw-simplification/references/dead-code-tools.md +23 -0
  60. package/scaffold/skills/dw-testing-discipline/SKILL.md +17 -0
  61. package/scaffold/skills/dw-ui-discipline/SKILL.md +1 -1
  62. package/scaffold/skills/dw-verify/SKILL.md +1 -1
  63. package/scaffold/skills/dw-verify/references/pre-check-and-status.md +28 -0
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "brunosps00-dev-workflow",
3
+ "owner": {
4
+ "name": "Bruno Santos"
5
+ },
6
+ "plugins": [
7
+ {
8
+ "name": "dev-workflow",
9
+ "source": "./",
10
+ "description": "Standalone, à-la-carte skills from the dev-workflow pipeline: minimalism, search-first, simplification, verification, security review, and more."
11
+ }
12
+ ]
13
+ }
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "dev-workflow",
3
+ "description": "Standalone, à-la-carte skills from the dev-workflow pipeline: minimalism, search-first, simplification, verification, security review, and more.",
4
+ "version": "1.5.0",
5
+ "author": {
6
+ "name": "Bruno Santos"
7
+ },
8
+ "homepage": "https://github.com/brunosantos/dev-workflow",
9
+ "license": "MIT",
10
+ "keywords": [
11
+ "ai",
12
+ "workflow",
13
+ "skills",
14
+ "claude",
15
+ "minimalism",
16
+ "verification",
17
+ "security"
18
+ ],
19
+ "skills": [
20
+ "./scaffold/skills/api-testing-recipes",
21
+ "./scaffold/skills/docker-compose-recipes",
22
+ "./scaffold/skills/dw-minimalism",
23
+ "./scaffold/skills/dw-search-first",
24
+ "./scaffold/skills/dw-simplification",
25
+ "./scaffold/skills/dw-verify",
26
+ "./scaffold/skills/humanizer",
27
+ "./scaffold/skills/remotion-best-practices",
28
+ "./scaffold/skills/security-review",
29
+ "./scaffold/skills/vercel-react-best-practices"
30
+ ]
31
+ }
package/README.md CHANGED
@@ -12,10 +12,11 @@ This will:
12
12
  1. Ask you to select a language (English or Portuguese)
13
13
  2. Create `.dw/commands/` with workflow commands
14
14
  3. Create `.dw/templates/` with document templates (PRD, TechSpec, Tasks, ADR, etc.)
15
- 4. Create `.dw/rules/` (populated by `/dw-analyze-project`)
15
+ 4. Create `.dw/rules/` (populated by `/dw-analyze-project`) + a curated `.dw/rules-library/` declarative baseline per stack (loaded lazily via `.dw/config/stack-mappings.json`)
16
16
  5. Install bundled skills (`dw-verify`, `dw-memory`, `dw-review-rigor`, `dw-ui-discipline`, `dw-testing-discipline`, `security-review`, etc.) to `.agents/skills/`
17
17
  6. Generate skill wrappers and project agents for Claude Code, Codex, Copilot, and OpenCode
18
18
  7. Configure MCP servers (Context7 + Playwright)
19
+ 8. Install enforcement hooks (git guardrails + a `SessionEnd` token-cost tracker → `.dw/metrics/costs.jsonl`) + a statusline (branch · active spec · minimalism mode · today's estimated spend) into `.claude/settings.json` (merge-aware — your own hooks/statusline are never overwritten)
19
20
 
20
21
  > **Compozy-inspired disciplines.** Since 0.5.0, dev-workflow bundles three primitives — `dw-verify`, `dw-memory`, `dw-review-rigor` — adapted from the [Compozy](https://github.com/compozy/compozy) project and invoked internally by existing commands. See [docs/compozy-integration.md](docs/compozy-integration.md) for what was ported and what was not.
21
22
 
@@ -24,9 +25,24 @@ Optional dependencies (Playwright browsers, react-doctor, Trivy, Semgrep, gitlea
24
25
  npx @brunosps00/dev-workflow install-deps
25
26
  ```
26
27
 
28
+ ### Install individual skills (à-la-carte)
29
+
30
+ Beyond the full pipeline, the standalone disciplines are also published as a Claude Code plugin, so you can adopt one skill without scaffolding the whole workflow:
31
+
32
+ ```bash
33
+ # Claude Code: add the marketplace, then install the plugin
34
+ /plugin marketplace add brunosps00/dev-workflow
35
+ /plugin install dev-workflow@brunosps00-dev-workflow
36
+
37
+ # or via the skills ecosystem
38
+ npx skills add brunosps00/dev-workflow
39
+ ```
40
+
41
+ Exportable skills (no `.dw/` pipeline required): `dw-minimalism`, `dw-search-first`, `dw-simplification`, `dw-verify`, `humanizer`, `security-review`, `vercel-react-best-practices`, `remotion-best-practices`, `api-testing-recipes`, `docker-compose-recipes`. The plugin manifest is generated from the skill registry (`npm run build:plugin`); pipeline-bound skills (`dw-execute-phase`, `dw-memory`, `dw-codebase-intel`, `dw-cli-run`) are intentionally not exported.
42
+
27
43
  ## Commands
28
44
 
29
- dev-workflow v1.3.0 ships **36 commands** organized into four tiers. Most users only invoke Tier 1 + Tier 2.
45
+ dev-workflow v1.5.0 ships **37 commands** organized into four tiers. Most users only invoke Tier 1 + Tier 2.
30
46
 
31
47
  ### Tier 1 — Gateway (3)
32
48
 
@@ -53,7 +69,7 @@ Use these when you want step-by-step control instead of `/dw-autopilot`.
53
69
  | **`/dw-commit`** | Atomic Conventional Commits for pending changes. Applies `dw-git-discipline` (one intent per commit, lint+tests+build green before). |
54
70
  | **`/dw-generate-pr [target]`** | Push the branch, draft a PR body with summary + test plan, open the browser. Hard gates: dw-verify PASS + secure-audit. |
55
71
 
56
- ### Tier 3 — Specialty (11)
72
+ ### Tier 3 — Specialty (12)
57
73
 
58
74
  | Command | What |
59
75
  |---------|------|
@@ -64,6 +80,7 @@ Use these when you want step-by-step control instead of `/dw-autopilot`.
64
80
  | **`/dw-context-budget`** | Audits context overhead from commands, skills, agents, instruction files, and MCPs. |
65
81
  | **`/dw-harness-audit`** | Deterministic health scorecard for the dev-workflow install, agents, wrappers, MCPs, and gates. |
66
82
  | **`/dw-skill-health`** | Audits installed skills and agents for bloat, duplication, and missing references. |
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**. |
67
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/`. |
68
85
  | **`/dw-dockerize`** | Reads existing project, detects stack + runtime deps, proposes Dockerfile + docker-compose for dev/prod with explicit trade-offs (Conservative/Balanced/Bold). |
69
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`. |
@@ -249,7 +266,7 @@ npx @brunosps00/dev-workflow subtask list
249
266
 
250
267
  ## Bundled Skills
251
268
 
252
- Skills installed to `.agents/skills/` for use by all commands. The bundle is governed by `scaffold/skill-registry.json`, which records each skill's kind, tier, owner, trigger, expected output, load policy, and context limit.
269
+ Skills installed to `.agents/skills/` for use by all commands. The bundle is governed by `scaffold/skill-registry.json`, which records each skill's kind, tier, owner, trigger, expected output, load policy, context limit, invocation control (`model` vs `explicit`), and à-la-carte `exportable` flag.
253
270
 
254
271
  Skill kinds:
255
272
 
@@ -274,6 +291,7 @@ These are not slash commands — they are primitives other commands call to enfo
274
291
  | **dw-execute-phase** | Goal-backward 6-dimension plan verification (`plan-checker`) and wave-based parallel task execution (`executor`) with atomic commit, deviation handling, and checkpoint recovery | `/dw-run`, `/dw-goal`, `/dw-autopilot` | [`get-shit-done-cc`](https://github.com/gsd-build/get-shit-done) (MIT) |
275
292
  | **dw-source-grounding** | Detect → Fetch → Implement → Cite protocol with `[source: <url>, version: X.Y, retrieved: YYYY-MM-DD]` citations. Strict source-priority hierarchy (Tier 1 official docs > Tier 2 maintainer blogs > Tier 3 Stack Overflow as discovery only). | `/dw-plan techspec`, `/dw-secure-audit --plan`, `/dw-opportunities --research`, `/dw-brainstorm --research` | [`addyosmani/agent-skills`](https://github.com/addyosmani/agent-skills) (MIT) |
276
293
  | **dw-simplification** | Chesterton's Fence (understand WHY before changing), behavior-preserving refactor protocol (test gate before/after), complexity metrics (cyclomatic, cognitive, depth, fan-out), Rule of 500 for large refactors | `/dw-review`, `/dw-refactor`, `/dw-brainstorm --mode=refactor-audit` | [`addyosmani/agent-skills`](https://github.com/addyosmani/agent-skills) (MIT) |
294
+ | **dw-minimalism** | Pre-generation YAGNI decision ladder (need it? reuse? stdlib? native? installed dep? one line?) with `lite`/`full`/`ultra` intensity. The missing rung between `dw-search-first` (dependencies) and `dw-simplification` (post-hoc cleanup). | `/dw-run`, `/dw-plan`, `/dw-review` | [`DietrichGebert/ponytail`](https://github.com/DietrichGebert/ponytail) (MIT) |
277
295
  | **dw-debug-protocol** | Stop-the-line discipline plus six-step triage (Reproduce → Localize → Reduce → Fix Root Cause → Guard → Verify End-to-End). Error categorization matrix; instrument-first non-reproducible-bug strategy. | `/dw-bugfix`, `/dw-qa --fix` | [`addyosmani/agent-skills`](https://github.com/addyosmani/agent-skills) (MIT) |
278
296
  | **dw-git-discipline** | Trunk-based pattern (1-3 day branches, daily rebase, feature flags), atomic commit discipline (one intent per commit; refactor separate from feature), Conventional Commits, branch hygiene | `dw-commit`, `dw-generate-pr` | [`addyosmani/agent-skills`](https://github.com/addyosmani/agent-skills) (MIT) |
279
297
 
@@ -347,6 +365,8 @@ Spec-Driven Development patterns — declarative constitution (`.dw/constitution
347
365
 
348
366
  UI discipline (`dw-ui-discipline`) and testing doctrine (`dw-testing-discipline`) are original works in this repository. Earlier dev-workflow versions (≤0.13.x) drew on `pedronauck/skills` `ui-craft` and `testing-boss` as inspiration, but in v0.14.0 those skills were rewritten clean-room after a license audit confirmed that the upstream repo has no explicit LICENSE file at root — the README's MIT claim is unverified. The underlying ideas (grounding before design; behavior over mocks; mutation over coverage) are widely-documented general software engineering principles available in many sources (Beck, Fowler, Meszaros, Feathers, Google SRE Book, WCAG specifications). Browser-DevTools patterns from [`addyosmani/agent-skills`](https://github.com/addyosmani/agent-skills) (MIT) live inside `dw-testing-discipline/references/` (`security-boundary.md`, `three-workflow-patterns.md`).
349
367
 
368
+ Minimalism discipline (`dw-minimalism`), per-skill invocation control (`disable-model-invocation` on hidden/internal command wrappers), the git-guardrails enforcement hook, and à-la-carte plugin distribution were adapted from [`DietrichGebert/ponytail`](https://github.com/DietrichGebert/ponytail) (MIT — the "lazy senior" decision ladder, intensity modes, and statusline) and [`mattpocock/skills`](https://github.com/mattpocock/skills) (MIT — user-vs-model invocation and `git-guardrails-claude-code`). See [docs/skills-ecosystem-comparison.md](docs/skills-ecosystem-comparison.md) for the full gap analysis and what was and was not adopted.
369
+
350
370
  Incident response (`dw-incident-response`) adapted from [`wilsto/claude-code-starter-kit/incident-response`](https://github.com/wilsto/claude-code-starter-kit) (MIT). The 5-phase workflow structure and runbook templates come from there. wilsto credits the upstream `wshobson/agents` plugin `incident-response` (v1.3.0); attribution chain preserved. Additional reading cited in the skill: Google SRE Book, Etsy Debriefing Facilitation Guide, PagerDuty Incident Response Documentation.
351
371
 
352
372
  LLM evaluation (`dw-llm-eval`) trajectory-match modes (strict / unordered / subset / superset) and tool-argument matching strategies adapted from [`langchain-ai/agentevals`](https://github.com/langchain-ai/agentevals) (MIT). The broader oracle-ladder framing, judge-calibration discipline, and reference-dataset principle are distilled from the open evaluations literature (OpenAI evals cookbook, Anthropic evals guidance, the academic eval-of-LLM body of work) and rewritten in our voice.
@@ -0,0 +1,79 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const pkg = require('../package.json');
4
+ const { readSkillRegistry } = require('./skill-registry');
5
+
6
+ /**
7
+ * Generate the à-la-carte distribution manifests from the skill registry.
8
+ *
9
+ * Single source of truth: scaffold/skill-registry.json. Only skills marked
10
+ * `exportable: true` (standalone, no .dw/ pipeline dependency) are published.
11
+ * Re-run `npm run build:plugin` whenever the registry changes; CI validates
12
+ * that the committed manifests match.
13
+ *
14
+ * Writes:
15
+ * .claude-plugin/plugin.json — the dev-workflow skills plugin
16
+ * .claude-plugin/marketplace.json — single-plugin marketplace listing
17
+ *
18
+ * This does NOT change the full-pipeline installer (`dev-workflow init`), which
19
+ * scaffolds all skills + commands into a project. The plugin is the parallel
20
+ * "install one skill" path.
21
+ */
22
+
23
+ const REPO_ROOT = path.join(__dirname, '..');
24
+ const PLUGIN_DIR = path.join(REPO_ROOT, '.claude-plugin');
25
+
26
+ function buildManifests() {
27
+ const registry = readSkillRegistry();
28
+ const exportable = registry.skills
29
+ .filter((s) => s.exportable === true)
30
+ .map((s) => s.name)
31
+ .sort();
32
+
33
+ const author = typeof pkg.author === 'string' ? { name: pkg.author } : pkg.author || { name: 'Bruno Santos' };
34
+
35
+ const plugin = {
36
+ name: 'dev-workflow',
37
+ description:
38
+ 'Standalone, à-la-carte skills from the dev-workflow pipeline: minimalism, search-first, simplification, verification, security review, and more.',
39
+ version: pkg.version,
40
+ author,
41
+ homepage: (pkg.repository && pkg.repository.url) || undefined,
42
+ license: pkg.license,
43
+ keywords: ['ai', 'workflow', 'skills', 'claude', 'minimalism', 'verification', 'security'],
44
+ skills: exportable.map((name) => `./scaffold/skills/${name}`),
45
+ };
46
+
47
+ const marketplace = {
48
+ name: 'brunosps00-dev-workflow',
49
+ owner: author,
50
+ plugins: [
51
+ {
52
+ name: 'dev-workflow',
53
+ source: './',
54
+ description: plugin.description,
55
+ },
56
+ ],
57
+ };
58
+
59
+ return { plugin, marketplace, exportable };
60
+ }
61
+
62
+ function writeJsonStable(filePath, obj) {
63
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
64
+ fs.writeFileSync(filePath, JSON.stringify(obj, null, 2) + '\n', 'utf-8');
65
+ }
66
+
67
+ function run() {
68
+ const { plugin, marketplace, exportable } = buildManifests();
69
+ writeJsonStable(path.join(PLUGIN_DIR, 'plugin.json'), plugin);
70
+ writeJsonStable(path.join(PLUGIN_DIR, 'marketplace.json'), marketplace);
71
+ console.log(`Wrote .claude-plugin/plugin.json + marketplace.json (${exportable.length} exportable skills):`);
72
+ for (const name of exportable) console.log(` - ${name}`);
73
+ }
74
+
75
+ module.exports = { buildManifests, run };
76
+
77
+ if (require.main === module) {
78
+ run();
79
+ }
package/lib/constants.js CHANGED
@@ -5,10 +5,10 @@ const COMMANDS = {
5
5
  { name: 'dw-autopilot', description: 'Trigger when user asks to implement, build, create, or add a feature non-trivially. First invocation plans and stops; second invocation resumes via dw-goal, then commit and PR.' },
6
6
  { name: 'dw-brainstorm', description: 'Refine a concrete idea against the product\'s existing features. Modes: ideation, --research, --onepager, --council. Use dw-opportunities before you have an idea and dw-refactor for code-health audits.' },
7
7
  { name: 'dw-bugfix', description: 'Trigger when user reports a bug, pastes an error, or describes broken behavior. Triages with three questions, then fixes or routes to PRD.' },
8
- { name: 'dw-claude-run', description: 'Trigger when user asks to run/fire Claude (claude -p headless) in a dedicated worktree to implement a prepared prompt/spec. Claude adapter over the dw-cli-run protocol: durable audit log, resumable session via --session-id, 0-10 dual evaluation, STOP for the gate. Never the main checkout; never merges.' },
9
- { name: 'dw-codex-run', description: 'Trigger when user asks to run/fire Codex (codex exec) in a dedicated worktree to implement a prepared prompt/spec. Codex adapter over the dw-cli-run protocol: durable audit log, resumable per-task session, 0-10 dual evaluation, STOP for the gate. Never the main checkout; never merges.' },
8
+ { name: 'dw-claude-run', userInvoked: true, description: 'Trigger when user asks to run/fire Claude (claude -p headless) in a dedicated worktree to implement a prepared prompt/spec. Claude adapter over the dw-cli-run protocol: durable audit log, resumable session via --session-id, 0-10 dual evaluation, STOP for the gate. Never the main checkout; never merges.' },
9
+ { name: 'dw-codex-run', userInvoked: true, description: 'Trigger when user asks to run/fire Codex (codex exec) in a dedicated worktree to implement a prepared prompt/spec. Codex adapter over the dw-cli-run protocol: durable audit log, resumable per-task session, 0-10 dual evaluation, STOP for the gate. Never the main checkout; never merges.' },
10
10
  { name: 'dw-commit', description: 'Trigger when implementation is complete and pending changes need to be committed. Atomic commits with Conventional Commits messages.' },
11
- { name: 'dw-copilot-run', description: 'Trigger when user asks to run/fire GitHub Copilot CLI (copilot -p) in a dedicated worktree to implement a prepared prompt/spec. Copilot adapter over the dw-cli-run protocol: durable audit log, resumable session, 0-10 dual evaluation, STOP for the gate. Never the main checkout; never merges.' },
11
+ { name: 'dw-copilot-run', userInvoked: true, description: 'Trigger when user asks to run/fire GitHub Copilot CLI (copilot -p) in a dedicated worktree to implement a prepared prompt/spec. Copilot adapter over the dw-cli-run protocol: durable audit log, resumable session, 0-10 dual evaluation, STOP for the gate. Never the main checkout; never merges.' },
12
12
  { name: 'dw-dockerize', description: 'Reads a project, detects the stack and runtime deps, then proposes Dockerfile and docker-compose for dev and prod with explicit trade-offs.' },
13
13
  { name: 'dw-find-skills', description: 'Searches the npx skills ecosystem for a skill matching what you need, vets it, and installs it where you choose.' },
14
14
  { name: 'dw-functional-doc', description: 'Maps screens and user flows into a functional doc, validated end-to-end with Playwright.' },
@@ -20,6 +20,7 @@ const COMMANDS = {
20
20
  { name: 'dw-install-aws-skills', description: 'Trigger when user asks to install AWS expertise, setup AWS MCP server, or add AWS agent skills. Opt-in: clones curated skills from aws/agent-toolkit-for-aws into .agents/skills/aws/ and registers the unified AWS MCP Server (stdio via mcp-proxy-for-aws). Requires uv, aws cli, and AWS credentials.' },
21
21
  { name: 'dw-install-azure-skills', description: 'Trigger when user asks to install Azure expertise, setup Microsoft docs MCP, or add Azure agent skills. Opt-in: clones curated skills from MicrosoftDocs/Agent-Skills into .agents/skills/azure/ and registers the Microsoft Learn MCP server.' },
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
+ { 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.' },
23
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.' },
24
25
  { 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.' },
25
26
  { 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.' },
@@ -32,9 +33,9 @@ const COMMANDS = {
32
33
  { name: 'dw-run', description: 'Trigger when user wants to execute tasks. Default runs all pending in dependency order; \'run <task-id>\' runs one; \'run --resume\' continues an interrupted plan.' },
33
34
  { name: 'dw-secure-audit', description: 'Security Gate: OWASP + Semgrep SAST (diff) + gitleaks secrets + Trivy SCA/IaC + lockfile + supply-chain + outdated. Rigoroso: secrets/CRITICAL/HIGH block. Auto-invoked by /dw-review and /dw-generate-pr, explicit phase in /dw-autopilot, and runnable standalone.' },
34
35
  { name: 'dw-skill-health', description: 'Audits installed skills and agents for bloat, duplication, missing references, and stale or unused modules.' },
35
- { name: 'dw-subtask-start', description: 'Creates a minimal input packet for a subagent without injecting the parent transcript.' },
36
- { name: 'dw-subtask-complete', description: 'Records a structured subagent handoff with result, files, decisions, risks, verification, and blockers.' },
37
- { name: 'dw-subtask-resume', description: 'Consumes pending subagent handoffs, summarizes them for the parent, and archives the local packets.' },
36
+ { name: 'dw-subtask-start', userInvoked: true, description: 'Creates a minimal input packet for a subagent without injecting the parent transcript.' },
37
+ { name: 'dw-subtask-complete', userInvoked: true, description: 'Records a structured subagent handoff with result, files, decisions, risks, verification, and blockers.' },
38
+ { name: 'dw-subtask-resume', userInvoked: true, description: 'Consumes pending subagent handoffs, summarizes them for the parent, and archives the local packets.' },
38
39
  { name: 'dw-update', description: 'Updates dev-workflow to the latest npm release in-place, with a snapshot you can roll back to.' },
39
40
  ],
40
41
  'pt-br': [
@@ -43,10 +44,10 @@ const COMMANDS = {
43
44
  { name: 'dw-autopilot', description: 'Trigger quando usuario pede pra implementar, criar ou adicionar uma feature nao-trivial. Primeira invocacao planeja e para; segunda retoma via dw-goal, depois commit e PR.' },
44
45
  { name: 'dw-brainstorm', description: 'Refina uma ideia concreta contra features existentes do produto. Modos: ideacao, --research, --onepager, --council. Use dw-opportunities antes de ter uma ideia e dw-refactor para auditoria de code health.' },
45
46
  { name: 'dw-bugfix', description: 'Trigger quando usuario reporta bug, cola erro ou descreve comportamento quebrado. Tria com tres perguntas, depois corrige ou roteia pra PRD.' },
46
- { name: 'dw-claude-run', description: 'Trigger quando usuario pede pra rodar/disparar o Claude (claude -p headless) numa worktree dedicada pra implementar um prompt/spec preparado. Adapter Claude sobre o protocolo dw-cli-run: log de auditoria duravel, sessao resumivel via --session-id, dupla avaliacao 0-10, PARA pro gate. Nunca no checkout principal; nunca mergeia.' },
47
- { name: 'dw-codex-run', description: 'Trigger quando usuario pede pra rodar/disparar o Codex (codex exec) numa worktree dedicada pra implementar um prompt/spec preparado. Adapter Codex sobre o protocolo dw-cli-run: log de auditoria duravel, sessao resumivel por tarefa, dupla avaliacao 0-10, PARA pro gate. Nunca no checkout principal; nunca mergeia.' },
47
+ { name: 'dw-claude-run', userInvoked: true, description: 'Trigger quando usuario pede pra rodar/disparar o Claude (claude -p headless) numa worktree dedicada pra implementar um prompt/spec preparado. Adapter Claude sobre o protocolo dw-cli-run: log de auditoria duravel, sessao resumivel via --session-id, dupla avaliacao 0-10, PARA pro gate. Nunca no checkout principal; nunca mergeia.' },
48
+ { name: 'dw-codex-run', userInvoked: true, description: 'Trigger quando usuario pede pra rodar/disparar o Codex (codex exec) numa worktree dedicada pra implementar um prompt/spec preparado. Adapter Codex sobre o protocolo dw-cli-run: log de auditoria duravel, sessao resumivel por tarefa, dupla avaliacao 0-10, PARA pro gate. Nunca no checkout principal; nunca mergeia.' },
48
49
  { name: 'dw-commit', description: 'Trigger quando implementacao esta completa e ha mudancas pendentes pra commit. Commits atomicos com mensagens Conventional Commits.' },
49
- { name: 'dw-copilot-run', description: 'Trigger quando usuario pede pra rodar/disparar o GitHub Copilot CLI (copilot -p) numa worktree dedicada pra implementar um prompt/spec preparado. Adapter Copilot sobre o protocolo dw-cli-run: log de auditoria duravel, sessao resumivel, dupla avaliacao 0-10, PARA pro gate. Nunca no checkout principal; nunca mergeia.' },
50
+ { name: 'dw-copilot-run', userInvoked: true, description: 'Trigger quando usuario pede pra rodar/disparar o GitHub Copilot CLI (copilot -p) numa worktree dedicada pra implementar um prompt/spec preparado. Adapter Copilot sobre o protocolo dw-cli-run: log de auditoria duravel, sessao resumivel, dupla avaliacao 0-10, PARA pro gate. Nunca no checkout principal; nunca mergeia.' },
50
51
  { name: 'dw-dockerize', description: 'Le um projeto, detecta stack e deps de runtime, e propoe Dockerfile e docker-compose para dev e prod com trade-offs explicitos.' },
51
52
  { name: 'dw-find-skills', description: 'Busca no ecossistema npx skills uma skill que resolva o que voce precisa, valida e instala onde voce escolher.' },
52
53
  { name: 'dw-functional-doc', description: 'Mapeia telas e fluxos em um dossie funcional, validado E2E com Playwright.' },
@@ -58,6 +59,7 @@ const COMMANDS = {
58
59
  { name: 'dw-install-aws-skills', description: 'Trigger quando usuario pede pra instalar expertise AWS, configurar MCP da AWS, ou adicionar agent skills AWS. Opt-in: clona skills curadas de aws/agent-toolkit-for-aws para .agents/skills/aws/ e registra o AWS MCP Server unificado (stdio via mcp-proxy-for-aws). Requer uv, aws cli e credenciais AWS.' },
59
60
  { name: 'dw-install-azure-skills', description: 'Trigger quando usuario pede pra instalar expertise Azure, configurar MCP do Microsoft docs, ou adicionar agent skills Azure. Opt-in: clona skills curadas de MicrosoftDocs/Agent-Skills para .agents/skills/azure/ e registra o Microsoft Learn MCP server.' },
60
61
  { name: 'dw-intel', description: 'Inteligencia do codebase: modo query (default) responde citando .dw/intel/ + .dw/rules/; modo --build (re)constroi indice.' },
62
+ { 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.' },
61
63
  { 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.' },
62
64
  { 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.' },
63
65
  { 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.' },
@@ -70,9 +72,9 @@ const COMMANDS = {
70
72
  { name: 'dw-run', description: 'Trigger quando usuario quer executar tasks. Default roda todas pendentes em ordem de dependencia; \'run <task-id>\' roda uma; \'run --resume\' continua plan interrompido.' },
71
73
  { name: 'dw-secure-audit', description: 'Security Gate: OWASP + Semgrep SAST (diff) + gitleaks secrets + Trivy SCA/IaC + lockfile + supply-chain + outdated. Rigoroso: secrets/CRITICAL/HIGH bloqueiam. Auto-invocado por /dw-review e /dw-generate-pr, fase explicita no /dw-autopilot, e executavel standalone.' },
72
74
  { name: 'dw-skill-health', description: 'Audita skills e agentes instalados por bloat, duplicacao, referencias quebradas e modulos sem uso.' },
73
- { name: 'dw-subtask-start', description: 'Cria um input packet minimo para subagente sem injetar transcript bruto do parent.' },
74
- { name: 'dw-subtask-complete', description: 'Registra handoff estruturado do subagente com resultado, arquivos, decisoes, riscos, verificacao e bloqueios.' },
75
- { name: 'dw-subtask-resume', description: 'Consome handoffs pendentes de subagentes, resume para o parent e arquiva os pacotes locais.' },
75
+ { name: 'dw-subtask-start', userInvoked: true, description: 'Cria um input packet minimo para subagente sem injetar transcript bruto do parent.' },
76
+ { name: 'dw-subtask-complete', userInvoked: true, description: 'Registra handoff estruturado do subagente com resultado, arquivos, decisoes, riscos, verificacao e bloqueios.' },
77
+ { name: 'dw-subtask-resume', userInvoked: true, description: 'Consome handoffs pendentes de subagentes, resume para o parent e arquiva os pacotes locais.' },
76
78
  { name: 'dw-update', description: 'Atualiza o dev-workflow para o release mais recente no npm, com snapshot para rollback.' },
77
79
  ],
78
80
  };
@@ -80,9 +82,12 @@ const COMMANDS = {
80
82
  const PLATFORMS = {
81
83
  claude: {
82
84
  dir: '.claude/skills',
83
- wrapperTemplate: (name, description) => `---
85
+ // opts.userInvoked → emit disable-model-invocation: true so Claude Code never
86
+ // auto-fires hidden/internal commands (e.g. the worktree runners); the user
87
+ // still invokes them explicitly with /<name>.
88
+ wrapperTemplate: (name, description, opts = {}) => `---
84
89
  name: ${name}
85
- description: "${description.replace(/"/g, '\\"')}"
90
+ description: "${description.replace(/"/g, '\\"')}"${opts.userInvoked ? '\ndisable-model-invocation: true' : ''}
86
91
  ---
87
92
 
88
93
  Read and follow ALL instructions in \`.dw/commands/${name}.md\`.
package/lib/doctor.js CHANGED
@@ -10,6 +10,7 @@ const {
10
10
  } = require('./agents');
11
11
  const { readInstallState, STATE_RELATIVE_PATH } = require('./install-state');
12
12
  const { validateSkillRegistry, readSkillRegistry, validateSkillStructuredReturn } = require('./skill-registry');
13
+ const { GIT_GUARDRAILS_CMD, STATUSLINE_CMD, HOOKS_DIR_MARKER } = require('./hooks');
13
14
  const removedCommands = require('./removed-commands');
14
15
 
15
16
  function run() {
@@ -33,6 +34,7 @@ function run() {
33
34
  checkSubtaskLayout(projectRoot, issues);
34
35
  checkSkillRegistry(projectRoot, issues, warnings);
35
36
  checkMcp(projectRoot, issues);
37
+ checkHooks(projectRoot, issues, warnings);
36
38
  checkLegacyCommands(projectRoot, warnings);
37
39
 
38
40
  console.log('dev-workflow doctor\n');
@@ -203,6 +205,37 @@ function checkMcp(projectRoot, issues) {
203
205
  }
204
206
  }
205
207
 
208
+ function checkHooks(projectRoot, issues, warnings) {
209
+ // Hook scripts (copied from scaffold/scripts/ into .dw/scripts/).
210
+ for (const script of ['git-guardrails.mjs', 'statusline.mjs']) {
211
+ const file = path.join(projectRoot, '.dw', 'scripts', 'hooks', script);
212
+ if (!fs.existsSync(file)) issues.push(`Missing hook script: ${path.relative(projectRoot, file)}`);
213
+ }
214
+
215
+ const settingsPath = path.join(projectRoot, '.claude', 'settings.json');
216
+ if (!fs.existsSync(settingsPath)) return; // checkMcp already reports this
217
+
218
+ let settings;
219
+ try {
220
+ settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
221
+ } catch {
222
+ return; // checkMcp already reports malformed settings
223
+ }
224
+
225
+ const preToolUse = (settings.hooks && settings.hooks.PreToolUse) || [];
226
+ const guardrailWired = preToolUse.some(
227
+ (g) => g && Array.isArray(g.hooks) && g.hooks.some((h) => h && h.command === GIT_GUARDRAILS_CMD),
228
+ );
229
+ if (!guardrailWired) issues.push('Missing git-guardrails PreToolUse hook in .claude/settings.json');
230
+
231
+ const statusLine = settings.statusLine;
232
+ if (!statusLine) {
233
+ issues.push('Missing statusLine in .claude/settings.json');
234
+ } else if (statusLine.command !== STATUSLINE_CMD && !String(statusLine.command || '').includes(HOOKS_DIR_MARKER)) {
235
+ warnings.push('Custom statusLine set; dev-workflow statusline is not active (add `node .dw/scripts/hooks/statusline.mjs`).');
236
+ }
237
+ }
238
+
206
239
  function checkLegacyCommands(projectRoot, warnings) {
207
240
  for (const entry of removedCommands) {
208
241
  const file = path.join(projectRoot, '.dw', 'commands', `${entry.name}.md`);
package/lib/hooks.js ADDED
@@ -0,0 +1,179 @@
1
+ const { readSettings, writeSettings } = require('./mcp');
2
+
3
+ // dev-workflow-owned hook/statusline scripts live under this path. The substring
4
+ // is the marker used to identify OUR entries in .claude/settings.json so we can
5
+ // add/refresh them on update without ever touching the user's own hooks.
6
+ const HOOKS_DIR_MARKER = '.dw/scripts/hooks/';
7
+ const GIT_GUARDRAILS_CMD = 'node .dw/scripts/hooks/git-guardrails.mjs';
8
+ const STATUSLINE_CMD = 'node .dw/scripts/hooks/statusline.mjs';
9
+ const GIT_GUARDRAILS_SCRIPT = 'git-guardrails.mjs';
10
+ const SESSION_COST_CMD = 'node .dw/scripts/hooks/session-cost.mjs';
11
+ const SESSION_COST_SCRIPT = 'session-cost.mjs';
12
+
13
+ function isOurs(command) {
14
+ return typeof command === 'string' && command.includes(HOOKS_DIR_MARKER);
15
+ }
16
+
17
+ /**
18
+ * Reconcile dev-workflow hooks + statusLine into .claude/settings.json.
19
+ *
20
+ * Marker-based and merge-aware (unlike installMCPs' add-if-missing):
21
+ * - adds our entries when absent;
22
+ * - refreshes our entries in place when the script path/shape changed (so an
23
+ * update actually updates);
24
+ * - never removes or rewrites hooks/statusLine the user added;
25
+ * - leaves a user-defined statusLine untouched (reports it so the caller can
26
+ * surface a post-update note).
27
+ *
28
+ * Returns { hooks, statusLine } where each is 'added' | 'updated' | 'unchanged'
29
+ * (statusLine may also be 'skipped-user').
30
+ */
31
+ function installHooks(projectRoot) {
32
+ const settings = readSettings(projectRoot);
33
+ const before = JSON.stringify(settings);
34
+
35
+ const hooksResult = reconcilePreToolUseGitGuardrails(settings);
36
+ const sessionCostResult = reconcileSessionEndCost(settings);
37
+ const statusLineResult = reconcileStatusLine(settings);
38
+
39
+ if (JSON.stringify(settings) !== before) {
40
+ writeSettings(projectRoot, settings);
41
+ }
42
+
43
+ return { hooks: hooksResult, sessionCost: sessionCostResult, statusLine: statusLineResult };
44
+ }
45
+
46
+ /**
47
+ * Reconcile the SessionEnd cost-tracking hook (marker-based, merge-aware — same
48
+ * contract as the git guardrail). No matcher: fires on every session end.
49
+ * Returns 'added' | 'updated' | 'unchanged'.
50
+ */
51
+ function reconcileSessionEndCost(settings) {
52
+ if (!settings.hooks || typeof settings.hooks !== 'object') settings.hooks = {};
53
+ if (!Array.isArray(settings.hooks.SessionEnd)) settings.hooks.SessionEnd = [];
54
+
55
+ const groups = settings.hooks.SessionEnd;
56
+
57
+ const existed = groups.some(
58
+ (g) => g && Array.isArray(g.hooks) && g.hooks.some((h) => h && typeof h.command === 'string' && h.command.includes(SESSION_COST_SCRIPT)),
59
+ );
60
+ const exactPresent = groups.some(
61
+ (g) => g && Array.isArray(g.hooks) && g.hooks.some((h) => h && h.command === SESSION_COST_CMD),
62
+ );
63
+
64
+ if (exactPresent) return 'unchanged';
65
+
66
+ // Drop any stale/relocated copies of OUR hook, preserving user hooks.
67
+ for (const group of groups) {
68
+ if (!group || !Array.isArray(group.hooks)) continue;
69
+ group.hooks = group.hooks.filter(
70
+ (h) => !(h && typeof h.command === 'string' && h.command.includes(SESSION_COST_SCRIPT)),
71
+ );
72
+ }
73
+ settings.hooks.SessionEnd = groups.filter((g) => g && Array.isArray(g.hooks) && g.hooks.length > 0);
74
+
75
+ settings.hooks.SessionEnd.push({ hooks: [{ type: 'command', command: SESSION_COST_CMD }] });
76
+
77
+ return existed ? 'updated' : 'added';
78
+ }
79
+
80
+ function reconcilePreToolUseGitGuardrails(settings) {
81
+ if (!settings.hooks || typeof settings.hooks !== 'object') settings.hooks = {};
82
+ if (!Array.isArray(settings.hooks.PreToolUse)) settings.hooks.PreToolUse = [];
83
+
84
+ const groups = settings.hooks.PreToolUse;
85
+
86
+ // Was our guardrail already wired (anywhere, any shape)?
87
+ const existed = groups.some(
88
+ (g) =>
89
+ g && Array.isArray(g.hooks) && g.hooks.some((h) => h && typeof h.command === 'string' && h.command.includes(GIT_GUARDRAILS_SCRIPT)),
90
+ );
91
+ // Already wired with the exact canonical command in a Bash group?
92
+ const exactPresent = groups.some(
93
+ (g) =>
94
+ g && g.matcher === 'Bash' && Array.isArray(g.hooks) && g.hooks.some((h) => h && h.command === GIT_GUARDRAILS_CMD),
95
+ );
96
+
97
+ if (exactPresent) return 'unchanged';
98
+
99
+ // Drop any stale/relocated copies of OUR guardrail, preserving user hooks.
100
+ for (const group of groups) {
101
+ if (!group || !Array.isArray(group.hooks)) continue;
102
+ group.hooks = group.hooks.filter(
103
+ (h) => !(h && typeof h.command === 'string' && h.command.includes(GIT_GUARDRAILS_SCRIPT)),
104
+ );
105
+ }
106
+ // Prune groups we just emptied (only those left with zero hooks).
107
+ settings.hooks.PreToolUse = groups.filter((g) => g && Array.isArray(g.hooks) && g.hooks.length > 0);
108
+
109
+ // Re-add the canonical entry: into an existing Bash group if present, else new.
110
+ const bashGroup = settings.hooks.PreToolUse.find((g) => g && g.matcher === 'Bash' && Array.isArray(g.hooks));
111
+ if (bashGroup) {
112
+ bashGroup.hooks.push({ type: 'command', command: GIT_GUARDRAILS_CMD });
113
+ } else {
114
+ settings.hooks.PreToolUse.push({ matcher: 'Bash', hooks: [{ type: 'command', command: GIT_GUARDRAILS_CMD }] });
115
+ }
116
+
117
+ return existed ? 'updated' : 'added';
118
+ }
119
+
120
+ function reconcileStatusLine(settings) {
121
+ const desired = { type: 'command', command: STATUSLINE_CMD };
122
+ const current = settings.statusLine;
123
+
124
+ if (!current) {
125
+ settings.statusLine = desired;
126
+ return 'added';
127
+ }
128
+ // Only manage a statusLine we own; never clobber a user's custom one.
129
+ if (current.command === STATUSLINE_CMD) return 'unchanged';
130
+ if (isOurs(current.command)) {
131
+ settings.statusLine = desired; // our script moved/renamed — refresh
132
+ return 'updated';
133
+ }
134
+ return 'skipped-user';
135
+ }
136
+
137
+ /**
138
+ * Remove ONLY dev-workflow-owned hook + statusLine entries (used by uninstall).
139
+ * Returns the number of entries removed.
140
+ */
141
+ function removeHooks(projectRoot) {
142
+ const settings = readSettings(projectRoot);
143
+ let removed = 0;
144
+
145
+ // Remove OUR hook entries from every event (PreToolUse, SessionEnd, PostToolUse, …),
146
+ // identified by the .dw/scripts/hooks/ marker; never touch user-added hooks.
147
+ if (settings.hooks && typeof settings.hooks === 'object') {
148
+ for (const event of Object.keys(settings.hooks)) {
149
+ const groups = settings.hooks[event];
150
+ if (!Array.isArray(groups)) continue;
151
+ for (const group of groups) {
152
+ if (!group || !Array.isArray(group.hooks)) continue;
153
+ const kept = group.hooks.filter((h) => !(h && isOurs(h.command)));
154
+ removed += group.hooks.length - kept.length;
155
+ group.hooks = kept;
156
+ }
157
+ settings.hooks[event] = groups.filter((g) => g && Array.isArray(g.hooks) && g.hooks.length > 0);
158
+ if (settings.hooks[event].length === 0) delete settings.hooks[event];
159
+ }
160
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
161
+ }
162
+
163
+ if (settings.statusLine && isOurs(settings.statusLine.command)) {
164
+ delete settings.statusLine;
165
+ removed++;
166
+ }
167
+
168
+ if (removed > 0) writeSettings(projectRoot, settings);
169
+ return removed;
170
+ }
171
+
172
+ module.exports = {
173
+ installHooks,
174
+ removeHooks,
175
+ HOOKS_DIR_MARKER,
176
+ GIT_GUARDRAILS_CMD,
177
+ STATUSLINE_CMD,
178
+ SESSION_COST_CMD,
179
+ };
package/lib/init.js CHANGED
@@ -4,6 +4,7 @@ const { ensureDir, copyDir, writeFile, upsertDelimitedBlock, log, listFilesRecur
4
4
  const { selectLanguage } = require('./prompts');
5
5
  const { generateWrappers } = require('./wrappers');
6
6
  const { installMCPs } = require('./mcp');
7
+ const { installHooks } = require('./hooks');
7
8
  const { generateAgents } = require('./agents');
8
9
  const { migrate: migrateGsd } = require('./migrate-gsd');
9
10
  const { migrate: migrateSkills } = require('./migrate-skills');
@@ -169,6 +170,30 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
169
170
  console.log();
170
171
  }
171
172
 
173
+ // 3.9. Copy curated rules library + stack mappings (declarative baseline per stack,
174
+ // loaded lazily by the detected stack). Managed: reconciled on update/repair.
175
+ const rulesLibSrc = path.join(SCAFFOLD_DIR, 'rules');
176
+ if (require('fs').existsSync(rulesLibSrc)) {
177
+ console.log(' Rules library:');
178
+ const rlResults = copyDir(rulesLibSrc, path.join(projectRoot, '.dw', 'rules-library'), managedForce);
179
+ totalCreated += rlResults.created;
180
+ totalSkipped += rlResults.skipped;
181
+ totalOverwritten += rlResults.overwritten;
182
+ touchedFiles.push(...listFilesRecursive(path.join(projectRoot, '.dw', 'rules-library')));
183
+ console.log(` ${rlResults.created} created, ${rlResults.skipped} skipped, ${rlResults.overwritten} overwritten\n`);
184
+ }
185
+ const stackMapSrc = path.join(SCAFFOLD_DIR, 'config', 'stack-mappings.json');
186
+ if (require('fs').existsSync(stackMapSrc)) {
187
+ const cfgDir = path.join(projectRoot, '.dw', 'config');
188
+ ensureDir(cfgDir);
189
+ const stackMapDest = path.join(cfgDir, 'stack-mappings.json');
190
+ const smStatus = writeFile(stackMapDest, require('fs').readFileSync(stackMapSrc, 'utf-8'), managedForce);
191
+ if (smStatus === 'created') totalCreated++;
192
+ else if (smStatus === 'overwritten') totalOverwritten++;
193
+ else totalSkipped++;
194
+ touchedFiles.push(stackMapDest);
195
+ }
196
+
172
197
  // 4. Create .dw/rules/ with README
173
198
  if (!isUpdate) {
174
199
  console.log(' Rules:');
@@ -211,6 +236,16 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
211
236
  console.log();
212
237
  }
213
238
 
239
+ // 5.15. Seed .dw/minimalism.json (init only; preserved on update). Holds the
240
+ // dw-minimalism intensity mode read by the skill and the statusline hook.
241
+ if (!isUpdate) {
242
+ const minimalismPath = path.join(projectRoot, '.dw', 'minimalism.json');
243
+ const minimalismStatus = writeFile(minimalismPath, JSON.stringify({ mode: 'full' }, null, 2) + '\n', false);
244
+ if (minimalismStatus === 'created') totalCreated++;
245
+ else totalSkipped++;
246
+ touchedFiles.push(minimalismPath);
247
+ }
248
+
214
249
  // 5.2. Create .dw/bugfixes/ with .gitkeep (init only; preserved on update)
215
250
  if (!isUpdate) {
216
251
  const bugfixesDir = path.join(projectRoot, '.dw', 'bugfixes');
@@ -222,6 +257,30 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
222
257
  touchedFiles.push(gitkeepPath);
223
258
  }
224
259
 
260
+ // 5.25. Ensure .dw/metrics/ + .gitignore (runtime cost tracking; costs.jsonl is
261
+ // machine-local so it is gitignored). Created if missing on both init and update.
262
+ {
263
+ const metricsDir = path.join(projectRoot, '.dw', 'metrics');
264
+ ensureDir(metricsDir);
265
+ const metricsGitignore = path.join(metricsDir, '.gitignore');
266
+ const mStatus = writeFile(metricsGitignore, 'costs.jsonl\n', false);
267
+ if (mStatus === 'created') totalCreated++;
268
+ else totalSkipped++;
269
+ touchedFiles.push(metricsGitignore);
270
+ }
271
+
272
+ // 5.26. Ensure .dw/memory/instincts/ (durable learnings promoted by /dw-learn;
273
+ // format in the dw-memory skill's references/instincts.md). Init only; preserved on update.
274
+ if (!isUpdate) {
275
+ const instinctsDir = path.join(projectRoot, '.dw', 'memory', 'instincts');
276
+ ensureDir(instinctsDir);
277
+ const instinctsKeep = path.join(instinctsDir, '.gitkeep');
278
+ const ikStatus = writeFile(instinctsKeep, '', false);
279
+ if (ikStatus === 'created') totalCreated++;
280
+ else totalSkipped++;
281
+ touchedFiles.push(instinctsKeep);
282
+ }
283
+
225
284
  // 5.3. Create local ephemeral subtask handoff directories.
226
285
  ensureSubtaskLayout(projectRoot);
227
286
  touchedFiles.push(path.join(projectRoot, '.dw', 'subtasks', '.gitignore'));
@@ -302,6 +361,15 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
302
361
  const mcpResults = installMCPs(projectRoot);
303
362
  console.log(` ${mcpResults.added} configured, ${mcpResults.skipped} already present\n`);
304
363
 
364
+ // 9.2. Reconcile dev-workflow hooks + statusline (marker-based, merge-aware:
365
+ // refreshes our entries, never touches user-defined hooks/statusLine).
366
+ console.log(' Hooks & statusline:');
367
+ const hookResults = installHooks(projectRoot);
368
+ console.log(` git-guardrails: ${hookResults.hooks}, session-cost: ${hookResults.sessionCost}, statusline: ${hookResults.statusLine}\n`);
369
+ if (hookResults.statusLine === 'skipped-user') {
370
+ console.log(' Note: a custom statusLine is set; to show dev-workflow state add `node .dw/scripts/hooks/statusline.mjs`.\n');
371
+ }
372
+
305
373
  // 9.5. Persist install state for diagnostics and repair.
306
374
  const installState = writeInstallState(projectRoot, {
307
375
  lang: selectedLang,