@fredericboyer/dev-team 0.1.2 → 0.3.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 (46) hide show
  1. package/README.md +128 -0
  2. package/bin/dev-team.js +1 -21
  3. package/dist/bin/dev-team.d.ts +1 -0
  4. package/dist/bin/dev-team.js +36 -0
  5. package/dist/bin/dev-team.js.map +1 -0
  6. package/dist/create-agent.d.ts +1 -0
  7. package/dist/create-agent.js +106 -0
  8. package/dist/create-agent.js.map +1 -0
  9. package/dist/files.d.ts +51 -0
  10. package/dist/files.js +155 -0
  11. package/dist/files.js.map +1 -0
  12. package/dist/init.d.ts +24 -0
  13. package/dist/init.js +358 -0
  14. package/dist/init.js.map +1 -0
  15. package/dist/prompts.d.ts +25 -0
  16. package/dist/prompts.js +109 -0
  17. package/dist/prompts.js.map +1 -0
  18. package/dist/scan.d.ts +15 -0
  19. package/dist/scan.js +187 -0
  20. package/dist/scan.js.map +1 -0
  21. package/dist/update.d.ts +5 -0
  22. package/dist/update.js +229 -0
  23. package/dist/update.js.map +1 -0
  24. package/package.json +30 -9
  25. package/templates/CLAUDE.md +21 -0
  26. package/templates/agent-memory/dev-team-architect/MEMORY.md +12 -0
  27. package/templates/agent-memory/dev-team-docs/MEMORY.md +12 -0
  28. package/templates/agent-memory/dev-team-lead/MEMORY.md +12 -0
  29. package/templates/agent-memory/dev-team-release/MEMORY.md +12 -0
  30. package/templates/agents/dev-team-architect.md +62 -0
  31. package/templates/agents/dev-team-deming.md +2 -1
  32. package/templates/agents/dev-team-docs.md +63 -0
  33. package/templates/agents/dev-team-lead.md +95 -0
  34. package/templates/agents/dev-team-release.md +65 -0
  35. package/templates/hooks/dev-team-post-change-review.js +63 -10
  36. package/templates/hooks/dev-team-pre-commit-gate.js +43 -14
  37. package/templates/hooks/dev-team-safety-guard.js +21 -11
  38. package/templates/hooks/dev-team-task-loop.js +17 -9
  39. package/templates/hooks/dev-team-tdd-enforce.js +42 -23
  40. package/templates/hooks/dev-team-watch-list.js +84 -0
  41. package/templates/settings.json +4 -0
  42. package/templates/skills/dev-team-audit/SKILL.md +85 -0
  43. package/templates/skills/dev-team-review/SKILL.md +68 -0
  44. package/lib/files.js +0 -160
  45. package/lib/init.js +0 -206
  46. package/lib/prompts.js +0 -123
@@ -12,13 +12,21 @@
12
12
  * Exit 2 = block, exit 0 = allow.
13
13
  */
14
14
 
15
- 'use strict';
15
+ "use strict";
16
16
 
17
- const { execFileSync } = require('child_process');
18
- const path = require('path');
17
+ const { execFileSync } = require("child_process");
18
+ const path = require("path");
19
19
 
20
- const input = JSON.parse(process.argv[2] || '{}');
21
- const filePath = (input.tool_input && (input.tool_input.file_path || input.tool_input.path)) || '';
20
+ let input = {};
21
+ try {
22
+ input = JSON.parse(process.argv[2] || "{}");
23
+ } catch (err) {
24
+ console.warn(
25
+ `[dev-team tdd-enforce] Warning: Failed to parse hook input, allowing operation. ${err.message}`,
26
+ );
27
+ process.exit(0);
28
+ }
29
+ const filePath = (input.tool_input && (input.tool_input.file_path || input.tool_input.path)) || "";
22
30
 
23
31
  if (!filePath) {
24
32
  process.exit(0);
@@ -28,20 +36,27 @@ const basename = path.basename(filePath);
28
36
  const ext = path.extname(filePath);
29
37
 
30
38
  // Skip non-code files
31
- const SKIP_EXTENSIONS = ['.md', '.json', '.yml', '.yaml', '.toml', '.txt', '.css', '.svg', '.png', '.jpg', '.gif', '.ico', '.lock'];
39
+ const SKIP_EXTENSIONS = [
40
+ ".md",
41
+ ".json",
42
+ ".yml",
43
+ ".yaml",
44
+ ".toml",
45
+ ".txt",
46
+ ".css",
47
+ ".svg",
48
+ ".png",
49
+ ".jpg",
50
+ ".gif",
51
+ ".ico",
52
+ ".lock",
53
+ ];
32
54
  if (SKIP_EXTENSIONS.includes(ext)) {
33
55
  process.exit(0);
34
56
  }
35
57
 
36
58
  // Skip if the file IS a test file
37
- const TEST_PATTERNS = [
38
- /\.test\./,
39
- /\.spec\./,
40
- /_test\./,
41
- /\/__tests__\//,
42
- /\/test\//,
43
- /\/tests\//,
44
- ];
59
+ const TEST_PATTERNS = [/\.test\./, /\.spec\./, /_test\./, /\/__tests__\//, /\/test\//, /\/tests\//];
45
60
 
46
61
  if (TEST_PATTERNS.some((p) => p.test(filePath))) {
47
62
  process.exit(0);
@@ -64,10 +79,10 @@ if (SKIP_PATTERNS.some((p) => p.test(filePath))) {
64
79
  }
65
80
 
66
81
  // Check if any test file has been modified in this session
67
- let changedFiles = '';
82
+ let changedFiles = "";
68
83
  try {
69
- changedFiles = execFileSync('git', ['diff', '--name-only'], {
70
- encoding: 'utf-8',
84
+ changedFiles = execFileSync("git", ["diff", "--name-only"], {
85
+ encoding: "utf-8",
71
86
  timeout: 5000,
72
87
  });
73
88
  } catch {
@@ -76,7 +91,7 @@ try {
76
91
  }
77
92
 
78
93
  const hasTestChanges = changedFiles
79
- .split('\n')
94
+ .split("\n")
80
95
  .filter(Boolean)
81
96
  .some((f) => TEST_PATTERNS.some((p) => p.test(f)));
82
97
 
@@ -88,19 +103,23 @@ if (hasTestChanges) {
88
103
  // No test changes — check if a corresponding test file already exists.
89
104
  // This allows refactoring (modifying existing tested code) without
90
105
  // requiring the test file to also be modified.
91
- const fs = require('fs');
106
+ const fs = require("fs");
92
107
  const dir = path.dirname(filePath);
93
108
  const name = path.basename(filePath, ext);
94
109
 
95
110
  const CANDIDATE_PATTERNS = [
96
111
  path.join(dir, `${name}.test${ext}`),
97
112
  path.join(dir, `${name}.spec${ext}`),
98
- path.join(dir, '__tests__', `${name}${ext}`),
99
- path.join(dir, '__tests__', `${name}.test${ext}`),
113
+ path.join(dir, "__tests__", `${name}${ext}`),
114
+ path.join(dir, "__tests__", `${name}.test${ext}`),
100
115
  ];
101
116
 
102
117
  const hasExistingTests = CANDIDATE_PATTERNS.some((candidate) => {
103
- try { return fs.statSync(candidate).isFile(); } catch { return false; }
118
+ try {
119
+ return fs.statSync(candidate).isFile();
120
+ } catch {
121
+ return false;
122
+ }
104
123
  });
105
124
 
106
125
  if (hasExistingTests) {
@@ -110,6 +129,6 @@ if (hasExistingTests) {
110
129
 
111
130
  // No test changes AND no existing test file — block
112
131
  console.error(
113
- `[dev-team tdd-enforce] TDD violation: "${basename}" modified but no corresponding test file exists. Write tests first.`
132
+ `[dev-team tdd-enforce] TDD violation: "${basename}" modified but no corresponding test file exists. Write tests first.`,
114
133
  );
115
134
  process.exit(2);
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * dev-team-watch-list.js
5
+ * PostToolUse hook on Edit/Write.
6
+ *
7
+ * Reads configurable file-pattern-to-agent mappings from .claude/dev-team.json
8
+ * and outputs structured spawn recommendations when patterns match.
9
+ * Advisory only — always exits 0.
10
+ *
11
+ * Config format in dev-team.json:
12
+ * {
13
+ * "watchLists": [
14
+ * { "pattern": "src/db/", "agents": ["dev-team-codd"], "reason": "database code changed" },
15
+ * { "pattern": "\\.graphql$", "agents": ["dev-team-mori", "dev-team-voss"], "reason": "API schema changed" }
16
+ * ]
17
+ * }
18
+ */
19
+
20
+ "use strict";
21
+
22
+ const fs = require("fs");
23
+ const path = require("path");
24
+
25
+ let input = {};
26
+ try {
27
+ input = JSON.parse(process.argv[2] || "{}");
28
+ } catch (err) {
29
+ console.warn(
30
+ `[dev-team watch-list] Warning: Failed to parse hook input, allowing operation. ${err.message}`,
31
+ );
32
+ process.exit(0);
33
+ }
34
+
35
+ const filePath = (input.tool_input && (input.tool_input.file_path || input.tool_input.path)) || "";
36
+
37
+ if (!filePath) {
38
+ process.exit(0);
39
+ }
40
+
41
+ // Read watch list config
42
+ let watchLists = [];
43
+ try {
44
+ const prefsPath = path.join(process.cwd(), ".claude", "dev-team.json");
45
+ const prefs = JSON.parse(fs.readFileSync(prefsPath, "utf-8"));
46
+ watchLists = prefs.watchLists || [];
47
+ } catch {
48
+ // No config or invalid — skip silently
49
+ process.exit(0);
50
+ }
51
+
52
+ if (watchLists.length === 0) {
53
+ process.exit(0);
54
+ }
55
+
56
+ const matches = [];
57
+
58
+ for (const entry of watchLists) {
59
+ if (!entry.pattern || !entry.agents) continue;
60
+
61
+ try {
62
+ const regex = new RegExp(entry.pattern);
63
+ if (regex.test(filePath)) {
64
+ for (const agent of entry.agents) {
65
+ if (!matches.some((m) => m.agent === agent)) {
66
+ matches.push({
67
+ agent,
68
+ reason: entry.reason || `file matched pattern: ${entry.pattern}`,
69
+ });
70
+ }
71
+ }
72
+ }
73
+ } catch {
74
+ // Invalid regex — skip this entry
75
+ continue;
76
+ }
77
+ }
78
+
79
+ if (matches.length > 0) {
80
+ const agentList = matches.map((m) => `@${m.agent} (${m.reason})`).join(", ");
81
+ console.log(`[dev-team watch-list] Spawn recommended: ${agentList}`);
82
+ }
83
+
84
+ process.exit(0);
@@ -11,6 +11,10 @@
11
11
  {
12
12
  "type": "command",
13
13
  "command": "node .claude/hooks/dev-team-tdd-enforce.js"
14
+ },
15
+ {
16
+ "type": "command",
17
+ "command": "node .claude/hooks/dev-team-watch-list.js"
14
18
  }
15
19
  ]
16
20
  }
@@ -0,0 +1,85 @@
1
+ ---
2
+ name: dev-team:audit
3
+ description: Full codebase audit combining security, quality, and tooling assessments. Use to run a comprehensive scan with Szabo (security), Knuth (quality), and Deming (tooling) in parallel. Can be scoped to specific directories or file patterns.
4
+ ---
5
+
6
+ Run a comprehensive audit of: $ARGUMENTS
7
+
8
+ ## Setup
9
+
10
+ 1. Determine the audit scope:
11
+ - If a directory or file pattern is given, scope the audit to those paths
12
+ - If no argument, audit the entire codebase
13
+ - Respect `.gitignore` — skip `node_modules/`, `dist/`, build artifacts
14
+
15
+ 2. The audit always spawns three specialist agents:
16
+ - **@dev-team-szabo** — Security audit
17
+ - **@dev-team-knuth** — Quality and correctness audit
18
+ - **@dev-team-deming** — Tooling and automation audit
19
+
20
+ ## Execution
21
+
22
+ 1. Spawn all three agents as **parallel background subagents** using the Agent tool with `subagent_type: "general-purpose"`.
23
+
24
+ 2. Each agent's prompt must include:
25
+ - The agent's full definition (read from `.claude/agents/<agent>.md`)
26
+ - The scope (directory/pattern or "full codebase")
27
+ - Instruction to produce classified findings: `[DEFECT]`, `[RISK]`, `[QUESTION]`, `[SUGGESTION]`
28
+ - Instruction to read the actual code and tests for full context
29
+
30
+ 3. Agent-specific instructions:
31
+
32
+ **Szabo (Security)**:
33
+ - Map all trust boundaries and entry points
34
+ - Check for OWASP Top 10 vulnerabilities
35
+ - Audit auth/authz flows end-to-end
36
+ - Review secret management and dependency vulnerabilities
37
+
38
+ **Knuth (Quality)**:
39
+ - Identify untested code paths and coverage gaps
40
+ - Find boundary conditions without tests
41
+ - Check assertion quality in existing tests
42
+ - Map test-to-requirement traceability
43
+
44
+ **Deming (Tooling)**:
45
+ - Inventory current tooling (linters, formatters, SAST, CI)
46
+ - Identify missing automation opportunities
47
+ - Check for stale dependencies and known vulnerabilities
48
+ - Evaluate CI pipeline efficiency
49
+
50
+ 4. Wait for all agents to complete.
51
+
52
+ ## Report
53
+
54
+ Produce a consolidated audit report:
55
+
56
+ ### Executive summary
57
+
58
+ One paragraph summarizing the overall health of the codebase across all three domains.
59
+
60
+ ### Security findings (@dev-team-szabo)
61
+
62
+ List all findings, grouped by classification:
63
+ - `[DEFECT]` — must fix
64
+ - `[RISK]` — should address
65
+ - `[QUESTION]` / `[SUGGESTION]` — consider
66
+
67
+ ### Quality findings (@dev-team-knuth)
68
+
69
+ Same grouping. Include specific files and line references.
70
+
71
+ ### Tooling findings (@dev-team-deming)
72
+
73
+ Same grouping. Include actionable recommendations.
74
+
75
+ ### Priority matrix
76
+
77
+ | Priority | Finding | Agent | Action |
78
+ |----------|---------|-------|--------|
79
+ | P0 (fix now) | `[DEFECT]` items | ... | ... |
80
+ | P1 (fix soon) | `[RISK]` items | ... | ... |
81
+ | P2 (improve) | `[SUGGESTION]` items | ... | ... |
82
+
83
+ ### Recommended next steps
84
+
85
+ Numbered list of concrete actions, ordered by priority. Each action should reference the specific finding it addresses.
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: dev-team:review
3
+ description: Orchestrated multi-agent parallel review. Use to review a PR, branch, or set of changes with multiple specialist agents simultaneously. Spawns agents based on changed file patterns and produces a unified review summary.
4
+ ---
5
+
6
+ Run a multi-agent parallel review of: $ARGUMENTS
7
+
8
+ ## Setup
9
+
10
+ 1. Determine what to review:
11
+ - If a PR number or branch is given, use `git diff` to get the changed files
12
+ - If a directory or file pattern is given, review those files
13
+ - If no argument, review all uncommitted changes (`git diff HEAD`)
14
+
15
+ 2. Categorize changed files by domain to determine which agents to spawn:
16
+
17
+ | File pattern | Agent | Reason |
18
+ |---|---|---|
19
+ | `auth`, `login`, `password`, `token`, `session`, `crypto`, `secret`, `permission`, `oauth`, `jwt`, `cors`, `csrf` | @dev-team-szabo | Security surface |
20
+ | `/api/`, `/routes/`, `schema`, `.graphql`, `.proto`, `openapi` | @dev-team-mori | API/UI contract |
21
+ | `docker`, `.env`, `config`, `migration`, `database`, `.sql`, `deploy` | @dev-team-voss | Infrastructure |
22
+ | `.github/workflows`, `.claude/`, `tsconfig`, `eslint`, `prettier`, `package.json` | @dev-team-deming | Tooling |
23
+ | `readme`, `changelog`, `.md`, `/docs/` | @dev-team-docs | Documentation |
24
+ | `/adr/`, `architecture`, `/core/`, `/domain/` | @dev-team-architect | Architecture |
25
+ | `package.json`, `version`, `changelog`, release workflows | @dev-team-release | Release artifacts |
26
+ | Any `.js`, `.ts`, `.py`, `.go`, `.java`, `.rs` (non-test) | @dev-team-knuth | Quality/coverage |
27
+
28
+ 3. Always include @dev-team-szabo and @dev-team-knuth — they review all code changes.
29
+
30
+ ## Execution
31
+
32
+ 1. Spawn each selected agent as a **parallel background subagent** using the Agent tool with `subagent_type: "general-purpose"`.
33
+
34
+ 2. Each agent's prompt must include:
35
+ - The agent's full definition (read from `.claude/agents/<agent>.md`)
36
+ - The list of changed files relevant to their domain
37
+ - Instruction to produce classified findings: `[DEFECT]`, `[RISK]`, `[QUESTION]`, `[SUGGESTION]`
38
+ - Instruction to read the actual code — not just the diff — for full context
39
+
40
+ 3. Wait for all agents to complete.
41
+
42
+ ## Report
43
+
44
+ Produce a unified review summary:
45
+
46
+ ### Blocking findings ([DEFECT])
47
+
48
+ List all `[DEFECT]` findings from all agents. These must be resolved before merge.
49
+
50
+ Format each as:
51
+ ```
52
+ **[DEFECT]** @agent-name — file:line
53
+ Description of the defect and why it blocks.
54
+ ```
55
+
56
+ ### Advisory findings
57
+
58
+ Group by severity:
59
+ - **[RISK]** — likely failure modes
60
+ - **[QUESTION]** — decisions needing justification
61
+ - **[SUGGESTION]** — specific improvements
62
+
63
+ ### Verdict
64
+
65
+ - **Approve** — No `[DEFECT]` findings. Advisory items noted.
66
+ - **Request changes** — `[DEFECT]` findings must be resolved.
67
+
68
+ State the verdict clearly. List what must be fixed for approval if requesting changes.
package/lib/files.js DELETED
@@ -1,160 +0,0 @@
1
- 'use strict';
2
-
3
- const fs = require('fs');
4
- const path = require('path');
5
-
6
- /**
7
- * Returns the absolute path to the templates/ directory within the package.
8
- */
9
- function templateDir() {
10
- return path.join(__dirname, '..', 'templates');
11
- }
12
-
13
- /**
14
- * Copies a file from src to dest, creating parent directories as needed.
15
- * Returns true if the file was written, false if skipped.
16
- */
17
- function copyFile(src, dest) {
18
- const dir = path.dirname(dest);
19
- fs.mkdirSync(dir, { recursive: true });
20
- fs.copyFileSync(src, dest);
21
- return true;
22
- }
23
-
24
- /**
25
- * Checks if a file exists.
26
- */
27
- function fileExists(absPath) {
28
- try {
29
- return fs.statSync(absPath).isFile();
30
- } catch {
31
- return false;
32
- }
33
- }
34
-
35
- /**
36
- * Checks if a directory exists.
37
- */
38
- function dirExists(absPath) {
39
- try {
40
- return fs.statSync(absPath).isDirectory();
41
- } catch {
42
- return false;
43
- }
44
- }
45
-
46
- /**
47
- * Reads a file and returns its content, or null if it doesn't exist.
48
- */
49
- function readFile(absPath) {
50
- try {
51
- return fs.readFileSync(absPath, 'utf-8');
52
- } catch {
53
- return null;
54
- }
55
- }
56
-
57
- /**
58
- * Writes content to a file, creating parent directories as needed.
59
- */
60
- function writeFile(absPath, content) {
61
- const dir = path.dirname(absPath);
62
- fs.mkdirSync(dir, { recursive: true });
63
- fs.writeFileSync(absPath, content);
64
- }
65
-
66
- /**
67
- * Deep merges hook configurations from source into target settings.
68
- * Additive only — never removes existing hooks.
69
- */
70
- function mergeSettings(existingPath, newFragment) {
71
- let existing = {};
72
- try {
73
- existing = JSON.parse(fs.readFileSync(existingPath, 'utf-8'));
74
- } catch {
75
- // File doesn't exist or is invalid — start fresh
76
- }
77
-
78
- if (!existing.hooks) {
79
- existing.hooks = {};
80
- }
81
-
82
- for (const [event, entries] of Object.entries(newFragment.hooks || {})) {
83
- if (!existing.hooks[event]) {
84
- existing.hooks[event] = entries;
85
- } else {
86
- // Add entries that don't already exist (by command string)
87
- for (const newEntry of entries) {
88
- const newCommands = (newEntry.hooks || []).map((h) => h.command);
89
- const alreadyExists = existing.hooks[event].some((existingEntry) => {
90
- const existingCommands = (existingEntry.hooks || []).map((h) => h.command);
91
- return newCommands.every((cmd) => existingCommands.includes(cmd));
92
- });
93
- if (!alreadyExists) {
94
- existing.hooks[event].push(newEntry);
95
- }
96
- }
97
- }
98
- }
99
-
100
- fs.writeFileSync(existingPath, JSON.stringify(existing, null, 2) + '\n');
101
- }
102
-
103
- /**
104
- * Appends content to a file with dev-team markers.
105
- * If markers already exist, replaces content between them.
106
- * If file doesn't exist, creates it with just the content.
107
- */
108
- function mergeClaudeMd(filePath, newContent) {
109
- const BEGIN_MARKER = '<!-- dev-team:begin -->';
110
- const END_MARKER = '<!-- dev-team:end -->';
111
-
112
- const existing = readFile(filePath);
113
-
114
- if (!existing) {
115
- // File doesn't exist — create with content
116
- writeFile(filePath, newContent);
117
- return 'created';
118
- }
119
-
120
- if (existing.includes(BEGIN_MARKER)) {
121
- // Markers exist — replace between them
122
- const beforeMarker = existing.substring(0, existing.indexOf(BEGIN_MARKER));
123
- const afterMarker = existing.substring(existing.indexOf(END_MARKER) + END_MARKER.length);
124
- writeFile(filePath, beforeMarker + newContent + afterMarker);
125
- return 'replaced';
126
- }
127
-
128
- // No markers — append
129
- writeFile(filePath, existing.trimEnd() + '\n\n' + newContent + '\n');
130
- return 'appended';
131
- }
132
-
133
- /**
134
- * Lists all files in a directory recursively.
135
- */
136
- function listFilesRecursive(dir) {
137
- const results = [];
138
- const entries = fs.readdirSync(dir, { withFileTypes: true });
139
- for (const entry of entries) {
140
- const fullPath = path.join(dir, entry.name);
141
- if (entry.isDirectory()) {
142
- results.push(...listFilesRecursive(fullPath));
143
- } else {
144
- results.push(fullPath);
145
- }
146
- }
147
- return results;
148
- }
149
-
150
- module.exports = {
151
- templateDir,
152
- copyFile,
153
- fileExists,
154
- dirExists,
155
- readFile,
156
- writeFile,
157
- mergeSettings,
158
- mergeClaudeMd,
159
- listFilesRecursive,
160
- };