@brunosps00/dev-workflow 1.2.1 → 1.4.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.
@@ -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.4.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
@@ -16,6 +16,7 @@ This will:
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 statusline 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.2.1 ships **33 commands** organized into four tiers. Most users only invoke Tier 1 + Tier 2.
45
+ dev-workflow v1.4.0 ships **36 commands** organized into four tiers. Most users only invoke Tier 1 + Tier 2.
30
46
 
31
47
  ### Tier 1 — Gateway (3)
32
48
 
@@ -69,7 +85,7 @@ Use these when you want step-by-step control instead of `/dw-autopilot`.
69
85
  | **`/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`. |
70
86
  | **`/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>]`. |
71
87
 
72
- ### Tier 4 — Hidden/Internal (9)
88
+ ### Tier 4 — Hidden/Internal (12)
73
89
 
74
90
  These are auto-invoked by Tier 1-3 commands. Available standalone via `/dw-help --advanced`.
75
91
 
@@ -79,6 +95,9 @@ These are auto-invoked by Tier 1-3 commands. Available standalone via `/dw-help
79
95
  | **`/dw-intel "question"`** | Query codebase intelligence (`.dw/intel/`). `--build` (re)creates the index. | `/dw-plan`, `/dw-review`, `/dw-bugfix` |
80
96
  | **`/dw-secure-audit`** | **Security Gate** (phase after review/QA, before commit/PR): OWASP + Semgrep SAST (diff/generated code) + gitleaks secrets + Trivy SCA/IaC + lockfile + supply-chain + outdated. Rigoroso: secrets/CRITICAL/HIGH block (secrets have no ADR escape). Flags: `--scan-only`, `--plan`, `--execute`. See [docs/security-gate.md](docs/security-gate.md). | `/dw-review`, `/dw-autopilot` (explicit step), enforced by `/dw-generate-pr` |
81
97
  | **`/dw-goal "<objective>"`** | Durable objective contract. Uses Codex native `/goal` when available and `.dw/goals/` everywhere for Codex, Copilot, Claude Code, and OpenCode. | `/dw-autopilot` after planning |
98
+ | **`/dw-claude-run`** | Fire `claude -p` (headless) in a dedicated git 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. | manual delegation step |
99
+ | **`/dw-codex-run`** | Fire `codex exec` in a dedicated git 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. | manual delegation step |
100
+ | **`/dw-copilot-run`** | Fire `copilot -p` (GitHub Copilot CLI) in a dedicated git 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. | manual delegation step |
82
101
  | **`/dw-find-skills "query"`** | Searches `npx skills` ecosystem, vets, installs. | manual when extending the bundle |
83
102
  | **`/dw-update`** | Updates dev-workflow to latest npm release with rollback snapshot. | manual maintenance |
84
103
  | **`/dw-subtask-start`** | Creates a minimal input packet for a subagent without injecting the parent transcript. | parent agent before delegation |
@@ -246,7 +265,7 @@ npx @brunosps00/dev-workflow subtask list
246
265
 
247
266
  ## Bundled Skills
248
267
 
249
- 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.
268
+ 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.
250
269
 
251
270
  Skill kinds:
252
271
 
@@ -271,6 +290,7 @@ These are not slash commands — they are primitives other commands call to enfo
271
290
  | **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) |
272
291
  | **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) |
273
292
  | **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) |
293
+ | **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) |
274
294
  | **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) |
275
295
  | **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) |
276
296
 
@@ -344,6 +364,8 @@ Spec-Driven Development patterns — declarative constitution (`.dw/constitution
344
364
 
345
365
  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`).
346
366
 
367
+ 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.
368
+
347
369
  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.
348
370
 
349
371
  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,7 +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', 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.' },
8
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', 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.' },
9
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.' },
10
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.' },
11
14
  { name: 'dw-functional-doc', description: 'Maps screens and user flows into a functional doc, validated end-to-end with Playwright.' },
@@ -29,9 +32,9 @@ const COMMANDS = {
29
32
  { 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.' },
30
33
  { 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.' },
31
34
  { name: 'dw-skill-health', description: 'Audits installed skills and agents for bloat, duplication, missing references, and stale or unused modules.' },
32
- { name: 'dw-subtask-start', description: 'Creates a minimal input packet for a subagent without injecting the parent transcript.' },
33
- { name: 'dw-subtask-complete', description: 'Records a structured subagent handoff with result, files, decisions, risks, verification, and blockers.' },
34
- { name: 'dw-subtask-resume', description: 'Consumes pending subagent handoffs, summarizes them for the parent, and archives the local packets.' },
35
+ { name: 'dw-subtask-start', userInvoked: true, description: 'Creates a minimal input packet for a subagent without injecting the parent transcript.' },
36
+ { name: 'dw-subtask-complete', userInvoked: true, description: 'Records a structured subagent handoff with result, files, decisions, risks, verification, and blockers.' },
37
+ { name: 'dw-subtask-resume', userInvoked: true, description: 'Consumes pending subagent handoffs, summarizes them for the parent, and archives the local packets.' },
35
38
  { name: 'dw-update', description: 'Updates dev-workflow to the latest npm release in-place, with a snapshot you can roll back to.' },
36
39
  ],
37
40
  'pt-br': [
@@ -40,7 +43,10 @@ const COMMANDS = {
40
43
  { 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.' },
41
44
  { 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.' },
42
45
  { 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', 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.' },
47
+ { 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.' },
43
48
  { 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', 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.' },
44
50
  { 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.' },
45
51
  { name: 'dw-find-skills', description: 'Busca no ecossistema npx skills uma skill que resolva o que voce precisa, valida e instala onde voce escolher.' },
46
52
  { name: 'dw-functional-doc', description: 'Mapeia telas e fluxos em um dossie funcional, validado E2E com Playwright.' },
@@ -64,9 +70,9 @@ const COMMANDS = {
64
70
  { 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.' },
65
71
  { 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.' },
66
72
  { name: 'dw-skill-health', description: 'Audita skills e agentes instalados por bloat, duplicacao, referencias quebradas e modulos sem uso.' },
67
- { name: 'dw-subtask-start', description: 'Cria um input packet minimo para subagente sem injetar transcript bruto do parent.' },
68
- { name: 'dw-subtask-complete', description: 'Registra handoff estruturado do subagente com resultado, arquivos, decisoes, riscos, verificacao e bloqueios.' },
69
- { name: 'dw-subtask-resume', description: 'Consome handoffs pendentes de subagentes, resume para o parent e arquiva os pacotes locais.' },
73
+ { name: 'dw-subtask-start', userInvoked: true, description: 'Cria um input packet minimo para subagente sem injetar transcript bruto do parent.' },
74
+ { name: 'dw-subtask-complete', userInvoked: true, description: 'Registra handoff estruturado do subagente com resultado, arquivos, decisoes, riscos, verificacao e bloqueios.' },
75
+ { name: 'dw-subtask-resume', userInvoked: true, description: 'Consome handoffs pendentes de subagentes, resume para o parent e arquiva os pacotes locais.' },
70
76
  { name: 'dw-update', description: 'Atualiza o dev-workflow para o release mais recente no npm, com snapshot para rollback.' },
71
77
  ],
72
78
  };
@@ -74,9 +80,12 @@ const COMMANDS = {
74
80
  const PLATFORMS = {
75
81
  claude: {
76
82
  dir: '.claude/skills',
77
- wrapperTemplate: (name, description) => `---
83
+ // opts.userInvoked → emit disable-model-invocation: true so Claude Code never
84
+ // auto-fires hidden/internal commands (e.g. the worktree runners); the user
85
+ // still invokes them explicitly with /<name>.
86
+ wrapperTemplate: (name, description, opts = {}) => `---
78
87
  name: ${name}
79
- description: "${description.replace(/"/g, '\\"')}"
88
+ description: "${description.replace(/"/g, '\\"')}"${opts.userInvoked ? '\ndisable-model-invocation: true' : ''}
80
89
  ---
81
90
 
82
91
  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,137 @@
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
+
11
+ function isOurs(command) {
12
+ return typeof command === 'string' && command.includes(HOOKS_DIR_MARKER);
13
+ }
14
+
15
+ /**
16
+ * Reconcile dev-workflow hooks + statusLine into .claude/settings.json.
17
+ *
18
+ * Marker-based and merge-aware (unlike installMCPs' add-if-missing):
19
+ * - adds our entries when absent;
20
+ * - refreshes our entries in place when the script path/shape changed (so an
21
+ * update actually updates);
22
+ * - never removes or rewrites hooks/statusLine the user added;
23
+ * - leaves a user-defined statusLine untouched (reports it so the caller can
24
+ * surface a post-update note).
25
+ *
26
+ * Returns { hooks, statusLine } where each is 'added' | 'updated' | 'unchanged'
27
+ * (statusLine may also be 'skipped-user').
28
+ */
29
+ function installHooks(projectRoot) {
30
+ const settings = readSettings(projectRoot);
31
+ const before = JSON.stringify(settings);
32
+
33
+ const hooksResult = reconcilePreToolUseGitGuardrails(settings);
34
+ const statusLineResult = reconcileStatusLine(settings);
35
+
36
+ if (JSON.stringify(settings) !== before) {
37
+ writeSettings(projectRoot, settings);
38
+ }
39
+
40
+ return { hooks: hooksResult, statusLine: statusLineResult };
41
+ }
42
+
43
+ function reconcilePreToolUseGitGuardrails(settings) {
44
+ if (!settings.hooks || typeof settings.hooks !== 'object') settings.hooks = {};
45
+ if (!Array.isArray(settings.hooks.PreToolUse)) settings.hooks.PreToolUse = [];
46
+
47
+ const groups = settings.hooks.PreToolUse;
48
+
49
+ // Was our guardrail already wired (anywhere, any shape)?
50
+ const existed = groups.some(
51
+ (g) =>
52
+ g && Array.isArray(g.hooks) && g.hooks.some((h) => h && typeof h.command === 'string' && h.command.includes(GIT_GUARDRAILS_SCRIPT)),
53
+ );
54
+ // Already wired with the exact canonical command in a Bash group?
55
+ const exactPresent = groups.some(
56
+ (g) =>
57
+ g && g.matcher === 'Bash' && Array.isArray(g.hooks) && g.hooks.some((h) => h && h.command === GIT_GUARDRAILS_CMD),
58
+ );
59
+
60
+ if (exactPresent) return 'unchanged';
61
+
62
+ // Drop any stale/relocated copies of OUR guardrail, preserving user hooks.
63
+ for (const group of groups) {
64
+ if (!group || !Array.isArray(group.hooks)) continue;
65
+ group.hooks = group.hooks.filter(
66
+ (h) => !(h && typeof h.command === 'string' && h.command.includes(GIT_GUARDRAILS_SCRIPT)),
67
+ );
68
+ }
69
+ // Prune groups we just emptied (only those left with zero hooks).
70
+ settings.hooks.PreToolUse = groups.filter((g) => g && Array.isArray(g.hooks) && g.hooks.length > 0);
71
+
72
+ // Re-add the canonical entry: into an existing Bash group if present, else new.
73
+ const bashGroup = settings.hooks.PreToolUse.find((g) => g && g.matcher === 'Bash' && Array.isArray(g.hooks));
74
+ if (bashGroup) {
75
+ bashGroup.hooks.push({ type: 'command', command: GIT_GUARDRAILS_CMD });
76
+ } else {
77
+ settings.hooks.PreToolUse.push({ matcher: 'Bash', hooks: [{ type: 'command', command: GIT_GUARDRAILS_CMD }] });
78
+ }
79
+
80
+ return existed ? 'updated' : 'added';
81
+ }
82
+
83
+ function reconcileStatusLine(settings) {
84
+ const desired = { type: 'command', command: STATUSLINE_CMD };
85
+ const current = settings.statusLine;
86
+
87
+ if (!current) {
88
+ settings.statusLine = desired;
89
+ return 'added';
90
+ }
91
+ // Only manage a statusLine we own; never clobber a user's custom one.
92
+ if (current.command === STATUSLINE_CMD) return 'unchanged';
93
+ if (isOurs(current.command)) {
94
+ settings.statusLine = desired; // our script moved/renamed — refresh
95
+ return 'updated';
96
+ }
97
+ return 'skipped-user';
98
+ }
99
+
100
+ /**
101
+ * Remove ONLY dev-workflow-owned hook + statusLine entries (used by uninstall).
102
+ * Returns the number of entries removed.
103
+ */
104
+ function removeHooks(projectRoot) {
105
+ const settings = readSettings(projectRoot);
106
+ let removed = 0;
107
+
108
+ if (settings.hooks && Array.isArray(settings.hooks.PreToolUse)) {
109
+ for (const group of settings.hooks.PreToolUse) {
110
+ if (!group || !Array.isArray(group.hooks)) continue;
111
+ const kept = group.hooks.filter((h) => !(h && isOurs(h.command)));
112
+ removed += group.hooks.length - kept.length;
113
+ group.hooks = kept;
114
+ }
115
+ settings.hooks.PreToolUse = settings.hooks.PreToolUse.filter(
116
+ (g) => g && Array.isArray(g.hooks) && g.hooks.length > 0,
117
+ );
118
+ if (settings.hooks.PreToolUse.length === 0) delete settings.hooks.PreToolUse;
119
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
120
+ }
121
+
122
+ if (settings.statusLine && isOurs(settings.statusLine.command)) {
123
+ delete settings.statusLine;
124
+ removed++;
125
+ }
126
+
127
+ if (removed > 0) writeSettings(projectRoot, settings);
128
+ return removed;
129
+ }
130
+
131
+ module.exports = {
132
+ installHooks,
133
+ removeHooks,
134
+ HOOKS_DIR_MARKER,
135
+ GIT_GUARDRAILS_CMD,
136
+ STATUSLINE_CMD,
137
+ };
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');
@@ -211,6 +212,16 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
211
212
  console.log();
212
213
  }
213
214
 
215
+ // 5.15. Seed .dw/minimalism.json (init only; preserved on update). Holds the
216
+ // dw-minimalism intensity mode read by the skill and the statusline hook.
217
+ if (!isUpdate) {
218
+ const minimalismPath = path.join(projectRoot, '.dw', 'minimalism.json');
219
+ const minimalismStatus = writeFile(minimalismPath, JSON.stringify({ mode: 'full' }, null, 2) + '\n', false);
220
+ if (minimalismStatus === 'created') totalCreated++;
221
+ else totalSkipped++;
222
+ touchedFiles.push(minimalismPath);
223
+ }
224
+
214
225
  // 5.2. Create .dw/bugfixes/ with .gitkeep (init only; preserved on update)
215
226
  if (!isUpdate) {
216
227
  const bugfixesDir = path.join(projectRoot, '.dw', 'bugfixes');
@@ -302,6 +313,15 @@ async function run({ force = false, lang = null, mode = 'init', profile = null,
302
313
  const mcpResults = installMCPs(projectRoot);
303
314
  console.log(` ${mcpResults.added} configured, ${mcpResults.skipped} already present\n`);
304
315
 
316
+ // 9.2. Reconcile dev-workflow hooks + statusline (marker-based, merge-aware:
317
+ // refreshes our entries, never touches user-defined hooks/statusLine).
318
+ console.log(' Hooks & statusline:');
319
+ const hookResults = installHooks(projectRoot);
320
+ console.log(` git-guardrails: ${hookResults.hooks}, statusline: ${hookResults.statusLine}\n`);
321
+ if (hookResults.statusLine === 'skipped-user') {
322
+ console.log(' Note: a custom statusLine is set; to show dev-workflow state add `node .dw/scripts/hooks/statusline.mjs`.\n');
323
+ }
324
+
305
325
  // 9.5. Persist install state for diagnostics and repair.
306
326
  const installState = writeInstallState(projectRoot, {
307
327
  lang: selectedLang,
@@ -9,6 +9,10 @@ const SKILLS_DIR = path.join(SCAFFOLD_DIR, 'skills');
9
9
 
10
10
  const VALID_KINDS = new Set(['protocol', 'domain-pack', 'recipe-pack', 'asset-pack']);
11
11
  const VALID_LOAD_POLICIES = new Set(['always-small', 'lazy-references', 'opt-in-runtime']);
12
+ // Optional invocation control (mattpocock/skills). Bundled skills live under
13
+ // .agents/skills/ (read by path from command prose), so this is documental for
14
+ // them and feeds the à-la-carte plugin manifest. Default when absent: 'model'.
15
+ const VALID_INVOCATIONS = new Set(['model', 'explicit']);
12
16
  const REQUIRED_STRUCTURED_RETURN_FIELDS = [
13
17
  '**Status:**',
14
18
  '**Scope:**',
@@ -106,6 +110,15 @@ function validateEntry(entry, issues) {
106
110
  if (typeof entry.context_limit !== 'number' || entry.context_limit <= 0) {
107
111
  issues.push(`Skill registry entry ${entry.name} must set a positive numeric context_limit`);
108
112
  }
113
+
114
+ // Optional fields (back-compat: absent is valid; defaults applied by consumers).
115
+ if (entry.invocation !== undefined && !VALID_INVOCATIONS.has(entry.invocation)) {
116
+ issues.push(`Skill registry entry ${entry.name} has invalid invocation: ${entry.invocation}`);
117
+ }
118
+
119
+ if (entry.exportable !== undefined && typeof entry.exportable !== 'boolean') {
120
+ issues.push(`Skill registry entry ${entry.name} exportable must be a boolean`);
121
+ }
109
122
  }
110
123
 
111
124
  function validateSkillStructuredReturn(skillName, skillPath, issues, label = `Skill ${skillName}`) {
package/lib/uninstall.js CHANGED
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { COMMANDS, PLATFORMS, MCP_SERVERS } = require('./constants');
4
4
  const { AGENT_TARGETS, LEGACY_AGENT_TARGETS, readAgentRegistry } = require('./agents');
5
+ const { removeHooks } = require('./hooks');
5
6
 
6
7
  function run() {
7
8
  const projectRoot = process.cwd();
@@ -103,6 +104,23 @@ function run() {
103
104
  }
104
105
  console.log();
105
106
 
107
+ // 4.5. Remove dev-workflow hooks + statusline from .claude/settings.json
108
+ // (marker-based; user-defined hooks/statusLine are left intact). The hook
109
+ // scripts themselves were removed with .dw/scripts/ above.
110
+ console.log(' Hooks & statusline:');
111
+ try {
112
+ const removedHooks = removeHooks(projectRoot);
113
+ if (removedHooks > 0) {
114
+ console.log(` \x1b[31m-\x1b[0m ${removedHooks} dev-workflow hook/statusline entr${removedHooks === 1 ? 'y' : 'ies'} [removed]`);
115
+ removed += removedHooks;
116
+ } else {
117
+ skipped++;
118
+ }
119
+ } catch {
120
+ console.log(' Could not parse .claude/settings.json, skipping hook cleanup');
121
+ }
122
+ console.log();
123
+
106
124
  // 5. Remove .opencode/package.json
107
125
  removeFile(path.join(projectRoot, '.opencode', 'package.json'));
108
126
  removeFile(path.join(projectRoot, '.dw', 'install-state.json'));
@@ -115,9 +133,10 @@ function run() {
115
133
  console.log(` Done! ${removed} removed, ${skipped} already absent`);
116
134
  console.log();
117
135
  console.log(' Preserved (user data):');
118
- console.log(' .dw/rules/ (project rules)');
119
- console.log(' .dw/spec/ (PRDs and specs)');
120
- console.log(' .dw/intel/ (codebase index from /dw-intel --build)');
136
+ console.log(' .dw/rules/ (project rules)');
137
+ console.log(' .dw/spec/ (PRDs and specs)');
138
+ console.log(' .dw/intel/ (codebase index from /dw-intel --build)');
139
+ console.log(' .dw/minimalism.json (dw-minimalism intensity preference)');
121
140
  console.log();
122
141
  }
123
142
 
@@ -0,0 +1,62 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { validateSkillRegistry } = require('./skill-registry');
4
+ const { validateAgentRegistry, readAgentRegistry } = require('./agents');
5
+ const { buildManifests } = require('./build-plugin');
6
+
7
+ /**
8
+ * Repo quality gate (npm run validate). Validates the skill + agent registries
9
+ * and confirms the committed .claude-plugin/ manifests match what the registry
10
+ * would generate (no drift). Exits non-zero on any issue.
11
+ */
12
+ function run() {
13
+ const issues = [];
14
+ const warnings = [];
15
+
16
+ const skill = validateSkillRegistry();
17
+ issues.push(...skill.issues);
18
+ warnings.push(...skill.warnings);
19
+
20
+ const agent = validateAgentRegistry(readAgentRegistry());
21
+ issues.push(...agent.issues);
22
+ warnings.push(...agent.warnings);
23
+
24
+ // Plugin manifest drift check.
25
+ const { plugin, marketplace } = buildManifests();
26
+ checkManifestDrift('.claude-plugin/plugin.json', plugin, issues);
27
+ checkManifestDrift('.claude-plugin/marketplace.json', marketplace, issues);
28
+
29
+ console.log('dev-workflow validate\n');
30
+ if (warnings.length) {
31
+ console.log('Warnings:');
32
+ for (const w of warnings) console.log(`- ${w}`);
33
+ console.log();
34
+ }
35
+ if (issues.length) {
36
+ console.log('Issues:');
37
+ for (const i of issues) console.log(`- ${i}`);
38
+ console.log();
39
+ console.log('FAIL');
40
+ process.exit(1);
41
+ }
42
+ console.log('PASS: skill + agent registries valid; plugin manifests in sync.');
43
+ }
44
+
45
+ function checkManifestDrift(relPath, expected, issues) {
46
+ const file = path.join(__dirname, '..', relPath);
47
+ if (!fs.existsSync(file)) {
48
+ issues.push(`Missing ${relPath}; run \`npm run build:plugin\`.`);
49
+ return;
50
+ }
51
+ const onDisk = fs.readFileSync(file, 'utf-8');
52
+ const want = JSON.stringify(expected, null, 2) + '\n';
53
+ if (onDisk !== want) {
54
+ issues.push(`${relPath} is out of sync with the registry; run \`npm run build:plugin\`.`);
55
+ }
56
+ }
57
+
58
+ module.exports = { run };
59
+
60
+ if (require.main === module) {
61
+ run();
62
+ }