bmad-method-test-architecture-enterprise 2.0.4-next.0 → 2.0.4-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/README.md +1 -0
- package/cli/framework.js +190 -0
- package/cli/lib/framework-setup.js +896 -0
- package/cli/lib/resolve-tea-config.js +1 -0
- package/cli/lib/test-design-parser.js +57 -5
- package/cli/lib/workflow-cli.js +9 -0
- package/cli/test-design.js +410 -0
- package/package.json +6 -2
- package/skills/bmad-testarch-framework/resources/setup-routing.md +2 -0
- package/skills/bmad-testarch-framework/resources/setup-state.md +9 -0
- package/skills/bmad-testarch-test-design/steps-c/step-03-risk-and-testability.md +6 -0
- package/skills/bmad-testarch-test-design/steps-c/step-04-coverage-plan.md +3 -0
- package/skills/bmad-testarch-test-design/steps-c/step-05-generate-output.md +2 -1
- package/skills/bmod-tea/bmod.toml +1 -1
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"name": "bmad-method-test-architecture-enterprise",
|
|
32
32
|
"source": "./",
|
|
33
33
|
"description": "Master Test Architect module for quality strategy, test generation in red and expand modes with automatic run-and-heal, combined framework and CI setup, CI/CD quality gates, and structured testing education. The CI and ATDD commands remain available with existing customizations. Part of the BMad Method ecosystem.",
|
|
34
|
-
"version": "2.0.4-next.
|
|
34
|
+
"version": "2.0.4-next.2",
|
|
35
35
|
"author": {
|
|
36
36
|
"name": "Murat K Ozcan (TEA Creator) & Brian (BMad) Madison"
|
|
37
37
|
},
|
package/README.md
CHANGED
|
@@ -114,6 +114,7 @@ npm install --save-dev bmad-method-test-architecture-enterprise
|
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
- [`tea-test-review`](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/reference/tea-test-review-cli/) runs the review skill against changed tests and returns a gate verdict.
|
|
117
|
+
- [`tea-framework`](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/reference/tea-framework-cli/) creates, resumes, validates, and edits test framework and CI setup with retained run evidence.
|
|
117
118
|
- [`tea-trace`](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/reference/tea-trace-cli/) maps requirements to existing evidence and returns a coverage gate.
|
|
118
119
|
- [`tea-evaluate`](https://bmad-code-org.github.io/bmad-method-test-architecture-enterprise/reference/tea-evaluate-cli/) checks, runs, scores, and compares behavioral evaluations.
|
|
119
120
|
|
package/cli/framework.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** tea-framework drives the packaged framework and CI skill with durable run evidence. */
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { Command } = require('commander');
|
|
8
|
+
const { resolveModel } = require('./lib/agent-adapters');
|
|
9
|
+
const { agentOptions, WorkflowError } = require('./lib/workflow-cli');
|
|
10
|
+
const { resolveTeaConfig } = require('./lib/resolve-tea-config');
|
|
11
|
+
const { addAgentOptions, resolveWorkflowSkill, runWithEvidence } = require('./lib/workflow-cli');
|
|
12
|
+
const {
|
|
13
|
+
SKILL_NAME,
|
|
14
|
+
SCOPES,
|
|
15
|
+
OPERATIONS,
|
|
16
|
+
projectPath,
|
|
17
|
+
readJournal,
|
|
18
|
+
prepareSetup,
|
|
19
|
+
setupPrompt,
|
|
20
|
+
inspectCompletion,
|
|
21
|
+
protectResultPath,
|
|
22
|
+
saveRequestSnapshot,
|
|
23
|
+
} = require('./lib/framework-setup');
|
|
24
|
+
|
|
25
|
+
function collect(value, previous) {
|
|
26
|
+
return [...previous, value];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function resultReport(result) {
|
|
30
|
+
return [
|
|
31
|
+
'# Framework and CI setup result',
|
|
32
|
+
'',
|
|
33
|
+
`Operation: ${result.operation}. Scope: ${result.scope}.`,
|
|
34
|
+
`Completed: ${result.completed ? 'yes' : 'no'}.`,
|
|
35
|
+
`Agent: ${result.agent}. Model: ${result.model ?? 'unspecified'}.`,
|
|
36
|
+
'',
|
|
37
|
+
'## Completion evidence',
|
|
38
|
+
'',
|
|
39
|
+
...result.artifacts.map((file) => `- ${file}`),
|
|
40
|
+
'',
|
|
41
|
+
...(result.issues.length > 0 ? ['## Unresolved work', '', ...result.issues.map((issue) => `- ${issue}`), ''] : []),
|
|
42
|
+
`Retained agent evidence: ${result.runDirectory}.`,
|
|
43
|
+
'',
|
|
44
|
+
'Create completion verifies the skill journal and referenced outputs, then reruns the frozen test commands. Native verification logs and agent transcripts are retained. Validate findings remain report outcomes.',
|
|
45
|
+
'',
|
|
46
|
+
].join('\n');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function main(argv = process.argv) {
|
|
50
|
+
const program = new Command();
|
|
51
|
+
program
|
|
52
|
+
.name('tea-framework')
|
|
53
|
+
.description('Create, resume, validate or edit test framework and CI setup using the packaged TEA skill.')
|
|
54
|
+
.option('--project-root <dir>', 'project whose framework and CI are being set up', process.cwd())
|
|
55
|
+
.option('--scope <scope>', `requested phases (${SCOPES.join('|')})`, 'framework')
|
|
56
|
+
.option('--operation <operation>', `requested operation (${OPERATIONS.join('|')})`, 'create')
|
|
57
|
+
.option('--input <path>', 'exact project artifact to edit or validate (repeatable)', collect, [])
|
|
58
|
+
.option('--instructions <path>', 'project file containing the setup request or requested edit')
|
|
59
|
+
.option('--skill-root <dir>', 'explicit skill copy; defaults to the packaged framework skill')
|
|
60
|
+
.option('--output <dir>', 'project directory for retained CLI reports and agent evidence')
|
|
61
|
+
.option('--json <path>', 'additional project-relative machine-readable result path')
|
|
62
|
+
.option('--allow-network', 'allow unrestricted outbound network access in the Codex workspace sandbox')
|
|
63
|
+
.option('--use-playwright-utils', 'enable Playwright utilities when applicable')
|
|
64
|
+
.option('--no-use-playwright-utils', 'disable Playwright utilities')
|
|
65
|
+
.option('--use-pactjs-utils', 'enable Pact utilities when applicable')
|
|
66
|
+
.option('--no-use-pactjs-utils', 'disable Pact utilities')
|
|
67
|
+
.exitOverride()
|
|
68
|
+
.configureOutput({ writeErr: () => {} });
|
|
69
|
+
addAgentOptions(program);
|
|
70
|
+
program.setOptionValueWithSource('retries', '0', 'default');
|
|
71
|
+
try {
|
|
72
|
+
program.parse(argv);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error.exitCode === 0) return 0;
|
|
75
|
+
process.stderr.write(`tea-framework: ${error.message}\n`);
|
|
76
|
+
return 2;
|
|
77
|
+
}
|
|
78
|
+
const options = program.opts();
|
|
79
|
+
let request;
|
|
80
|
+
let jsonPath;
|
|
81
|
+
try {
|
|
82
|
+
if (options.allowNetwork && options.agent !== 'codex') throw new Error('--allow-network requires --agent codex');
|
|
83
|
+
if (options.allowNetwork) options.agentArg = ['-c', 'sandbox_workspace_write.network_access=true', ...options.agentArg];
|
|
84
|
+
const projectRoot = fs.realpathSync(path.resolve(options.projectRoot));
|
|
85
|
+
agentOptions(options, projectRoot);
|
|
86
|
+
if (!fs.statSync(projectRoot).isDirectory()) throw new Error('--project-root must be a directory');
|
|
87
|
+
const skillRoot = resolveWorkflowSkill({ projectRoot, skillName: SKILL_NAME, skillRoot: options.skillRoot });
|
|
88
|
+
const config = resolveTeaConfig({ projectRoot, skillRoot, skillName: SKILL_NAME, flags: options });
|
|
89
|
+
request = prepareSetup({ projectRoot, skillRoot, config, options });
|
|
90
|
+
const evidenceRoot = projectPath(projectRoot, options.output ?? path.join(request.artifactsRoot, 'framework', 'cli-runs'));
|
|
91
|
+
jsonPath = options.json ? projectPath(projectRoot, options.json) : null;
|
|
92
|
+
if (
|
|
93
|
+
jsonPath &&
|
|
94
|
+
(path.extname(jsonPath) !== '.json' ||
|
|
95
|
+
jsonPath === request.journalPath ||
|
|
96
|
+
request.inputs.includes(jsonPath) ||
|
|
97
|
+
jsonPath === request.instructionsPath)
|
|
98
|
+
) {
|
|
99
|
+
throw new Error('--json must name a .json result file separate from setup inputs and journal');
|
|
100
|
+
}
|
|
101
|
+
protectResultPath(request, jsonPath);
|
|
102
|
+
if (options.agent === 'none') {
|
|
103
|
+
process.stdout.write(`${setupPrompt(request)}\n`);
|
|
104
|
+
return 0;
|
|
105
|
+
}
|
|
106
|
+
const execution = runWithEvidence({
|
|
107
|
+
name: 'tea-framework',
|
|
108
|
+
projectRoot,
|
|
109
|
+
evidenceRoot,
|
|
110
|
+
options,
|
|
111
|
+
capabilities: ['command-execution'],
|
|
112
|
+
prepare: ({ attempt }) => ({ prompt: setupPrompt(request, { retry: attempt > 1 }), request }),
|
|
113
|
+
validate: ({ attemptDir }) => {
|
|
114
|
+
saveRequestSnapshot(request);
|
|
115
|
+
try {
|
|
116
|
+
protectResultPath(request, jsonPath, readJournal(projectPath(projectRoot, request.journalPath))?.data);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
jsonPath = null;
|
|
119
|
+
throw new WorkflowError('usage', error.message, { cause: error });
|
|
120
|
+
}
|
|
121
|
+
return inspectCompletion(request, { evidenceDir: attemptDir, timeoutMs: Number(options.timeoutMs) });
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
const outcome = execution.value;
|
|
125
|
+
const result = {
|
|
126
|
+
schema_version: '0.1.0',
|
|
127
|
+
completed: outcome.completed,
|
|
128
|
+
scope: request.scope,
|
|
129
|
+
operation: request.savedOperation,
|
|
130
|
+
agent: options.agent,
|
|
131
|
+
model: resolveModel(options.agent, options.model, options.agentArg),
|
|
132
|
+
runDirectory: execution.runDir,
|
|
133
|
+
journal: request.journalPath,
|
|
134
|
+
artifacts: outcome.artifacts,
|
|
135
|
+
issues: outcome.issues,
|
|
136
|
+
verification: outcome.executions,
|
|
137
|
+
};
|
|
138
|
+
const serialized = `${JSON.stringify(result, null, 2)}\n`;
|
|
139
|
+
fs.writeFileSync(path.join(execution.runDir, 'result.json'), serialized);
|
|
140
|
+
fs.writeFileSync(path.join(execution.runDir, 'report.md'), resultReport(result));
|
|
141
|
+
if (jsonPath) {
|
|
142
|
+
protectResultPath(request, jsonPath, readJournal(projectPath(projectRoot, request.journalPath))?.data);
|
|
143
|
+
fs.mkdirSync(path.dirname(jsonPath), { recursive: true });
|
|
144
|
+
fs.writeFileSync(jsonPath, serialized);
|
|
145
|
+
}
|
|
146
|
+
process.stdout.write(serialized);
|
|
147
|
+
return result.completed ? 0 : 1;
|
|
148
|
+
} catch (error) {
|
|
149
|
+
if (request) {
|
|
150
|
+
try {
|
|
151
|
+
saveRequestSnapshot(request);
|
|
152
|
+
} catch (snapshotError) {
|
|
153
|
+
process.stderr.write(`tea-framework: could not retain setup request: ${snapshotError.message}\n`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (error.runDir) {
|
|
157
|
+
const failureResult = {
|
|
158
|
+
schema_version: '0.1.0',
|
|
159
|
+
completed: false,
|
|
160
|
+
scope: request?.scope ?? options.scope,
|
|
161
|
+
operation: request?.savedOperation ?? options.operation,
|
|
162
|
+
agent: options.agent,
|
|
163
|
+
model: null,
|
|
164
|
+
runDirectory: error.runDir,
|
|
165
|
+
journal: request?.journalPath ?? null,
|
|
166
|
+
artifacts: [],
|
|
167
|
+
issues: [error.message],
|
|
168
|
+
failureClass: error.failureClass,
|
|
169
|
+
};
|
|
170
|
+
const serialized = `${JSON.stringify(failureResult, null, 2)}\n`;
|
|
171
|
+
fs.writeFileSync(path.join(error.runDir, 'result.json'), serialized);
|
|
172
|
+
fs.writeFileSync(path.join(error.runDir, 'report.md'), resultReport(failureResult));
|
|
173
|
+
if (jsonPath) {
|
|
174
|
+
try {
|
|
175
|
+
protectResultPath(request, jsonPath, readJournal(projectPath(request.projectRoot, request.journalPath))?.data);
|
|
176
|
+
fs.mkdirSync(path.dirname(jsonPath), { recursive: true });
|
|
177
|
+
fs.writeFileSync(jsonPath, serialized);
|
|
178
|
+
} catch (publicationError) {
|
|
179
|
+
process.stderr.write(`tea-framework: result publication refused: ${publicationError.message}\n`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
process.stdout.write(serialized);
|
|
183
|
+
}
|
|
184
|
+
process.stderr.write(`tea-framework: ${error.message}${error.runDir ? `\nRetained evidence: ${error.runDir}` : ''}\n`);
|
|
185
|
+
return ['environment-transport', 'environment-timeout', 'environment-parser'].includes(error.failureClass) ? 3 : 2;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (require.main === module) process.exitCode = main();
|
|
190
|
+
module.exports = { main, resultReport };
|