create-byan-agent 2.19.1 → 2.20.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.
- package/CHANGELOG.md +188 -0
- package/README.md +4 -4
- package/install/src/byan-v2/generation/templates/default-agent.md +1 -1
- package/install/templates/.claude/CLAUDE.md +1 -1
- package/install/templates/.claude/hooks/fd-phase-guard.js +2 -2
- package/install/templates/.claude/hooks/mantra-validate.js +16 -8
- package/install/templates/.claude/hooks/strict-scope-guard.js +25 -7
- package/install/templates/.claude/rules/native-workflows.md +32 -0
- package/install/templates/.claude/skills/byan-byan/SKILL.md +5 -5
- package/install/templates/.claude/skills/byan-mantra-audit/SKILL.md +53 -0
- package/install/templates/.claude/skills/byan-merise-agile/SKILL.md +2 -2
- package/install/templates/.claude/skills/byan-native-dev-story/SKILL.md +83 -0
- package/install/templates/.claude/workflows/INDEX.md +35 -0
- package/install/templates/.claude/workflows/check-implementation-readiness.js +280 -0
- package/install/templates/.claude/workflows/code-review.js +179 -0
- package/install/templates/.claude/workflows/create-excalidraw-dataflow.js +214 -0
- package/install/templates/.claude/workflows/create-excalidraw-diagram.js +188 -0
- package/install/templates/.claude/workflows/create-excalidraw-flowchart.js +225 -0
- package/install/templates/.claude/workflows/create-excalidraw-wireframe.js +192 -0
- package/install/templates/.claude/workflows/create-story.js +216 -0
- package/install/templates/.claude/workflows/dev-story.js +100 -0
- package/install/templates/.claude/workflows/document-project.js +455 -0
- package/install/templates/.claude/workflows/qa-automate.js +169 -0
- package/install/templates/.claude/workflows/quick-dev.js +273 -0
- package/install/templates/.claude/workflows/sprint-planning.js +261 -0
- package/install/templates/.claude/workflows/testarch-atdd.js +287 -0
- package/install/templates/.claude/workflows/testarch-automate.js +229 -0
- package/install/templates/.claude/workflows/testarch-ci.js +184 -0
- package/install/templates/.claude/workflows/testarch-framework.js +267 -0
- package/install/templates/.claude/workflows/testarch-nfr.js +316 -0
- package/install/templates/.claude/workflows/testarch-test-design.js +293 -0
- package/install/templates/.claude/workflows/testarch-test-review.js +321 -0
- package/install/templates/.claude/workflows/testarch-trace.js +316 -0
- package/install/templates/.githooks/pre-commit +49 -15
- package/install/templates/_byan/config.yaml +15 -5
- package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-build-workflows.js +20 -0
- package/install/templates/_byan/mcp/byan-mcp-server/bin/byan-lint-workflows.js +57 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/native-loop.js +39 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-generator.js +149 -0
- package/install/templates/_byan/mcp/byan-mcp-server/lib/workflows-lint.js +113 -0
- package/install/templates/_byan/workflow/simple/byan/feature-workflow.md +14 -11
- package/install/templates/docs/native-workflows-contract.md +84 -0
- package/package.json +2 -2
- package/src/byan-v2/data/agent-scopes.json +46 -0
- package/src/byan-v2/data/mantras.json +194 -8
- package/src/byan-v2/generation/mantra-audit.js +147 -0
- package/src/byan-v2/generation/mantra-validator.js +56 -6
- package/src/byan-v2/generation/scope-resolver.js +102 -0
- package/src/byan-v2/generation/templates/default-agent.md +1 -1
- package/update-byan-agent/bin/update-byan-agent.js +67 -72
- package/update-byan-agent/lib/apply-update.js +202 -0
- package/update-byan-agent/package.json +1 -1
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'document-project',
|
|
3
|
+
description: 'Faithful native port of the BYAN document-project full-scan pipeline: scan a brownfield codebase and emit comprehensive reference docs (overview, tech stack, source tree, conditional analysis, per-part architecture, master index) for AI-assisted development. Mirrors full-scan-instructions.md steps 1-12. Human gates (scan-level choice, classification confirmation, review iteration) stay OUT of the script and surface as a verdict.',
|
|
4
|
+
phases: [
|
|
5
|
+
{ title: 'CLASSIFY' },
|
|
6
|
+
{ title: 'EXISTING_DOCS' },
|
|
7
|
+
{ title: 'TECH_STACK' },
|
|
8
|
+
{ title: 'CONDITIONAL_ANALYSIS' },
|
|
9
|
+
{ title: 'SOURCE_TREE' },
|
|
10
|
+
{ title: 'DEV_OPS' },
|
|
11
|
+
{ title: 'INTEGRATION' },
|
|
12
|
+
{ title: 'ARCHITECTURE' },
|
|
13
|
+
{ title: 'SUPPORTING_DOCS' },
|
|
14
|
+
{ title: 'MASTER_INDEX' },
|
|
15
|
+
{ title: 'VALIDATE' },
|
|
16
|
+
{ title: 'FINALIZE' }
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// FD/STRICT CONTRACT (re-asserted): this script returns data only. It NEVER imports
|
|
21
|
+
// or writes lib/fd-state.js / fd-state.json, and records no platform/strict state.
|
|
22
|
+
// The orchestrating skill records FD/strict state via MCP at the human gate. The
|
|
23
|
+
// script also uses no wall-clock and no randomness (both break Workflow resume);
|
|
24
|
+
// any timestamp/id is passed in via args. The byan-lint-workflows linter forbids
|
|
25
|
+
// fd-state coupling here.
|
|
26
|
+
|
|
27
|
+
// Inputs (passed by the orchestrating skill; no fs / no clock / no RNG here).
|
|
28
|
+
const projectRoot = (args && args.projectRoot) || process.cwd()
|
|
29
|
+
const outputFolder = (args && args.outputFolder) || '_byan-output/project-documentation'
|
|
30
|
+
// scan_level mirrors the source human gate (quick | deep | exhaustive). The script
|
|
31
|
+
// does NOT prompt for it; it threads the choice through. Default = quick (source default).
|
|
32
|
+
const scanLevel = (args && args.scanLevel) || 'quick'
|
|
33
|
+
// workflow_mode mirrors the source router: initial_scan | full_rescan. deep_dive is a
|
|
34
|
+
// separate sub-workflow and is intentionally out of this full-scan port's scope.
|
|
35
|
+
const workflowMode = (args && args.workflowMode) || 'initial_scan'
|
|
36
|
+
const runDate = (args && args.date) || 'unspecified'
|
|
37
|
+
|
|
38
|
+
const reqCsv = projectRoot + '/_byan/workflow/simple/document-project/documentation-requirements.csv'
|
|
39
|
+
|
|
40
|
+
log('document-project full-scan port — mode=' + workflowMode + ' scan_level=' + scanLevel)
|
|
41
|
+
log('project_root=' + projectRoot + ' output=' + outputFolder)
|
|
42
|
+
|
|
43
|
+
// STEP 1 — Detect project structure and classify project type.
|
|
44
|
+
phase('CLASSIFY')
|
|
45
|
+
const classification = await agent(
|
|
46
|
+
'You are documenting the brownfield project rooted at "' + projectRoot + '". ' +
|
|
47
|
+
'STEP 1 (document-project full-scan): detect repository structure and classify project type(s). ' +
|
|
48
|
+
'Scan the root for directory markers (client/ server/ api/ src/ app/ packages/) and key manifest files ' +
|
|
49
|
+
'(package.json, go.mod, requirements.txt, Cargo.toml, pom.xml, etc.). ' +
|
|
50
|
+
'Decide repository_type: monolith | monorepo | multi-part. For each distinct part, identify its root path and ' +
|
|
51
|
+
'match detected tech/file patterns against key_file_patterns in the documentation-requirements CSV at "' + reqCsv + '" ' +
|
|
52
|
+
'(12 project types: web, mobile, backend, cli, library, desktop, game, data, extension, infra, embedded, and a fallback). ' +
|
|
53
|
+
'Assign a project_type_id and display_name to each part. ' +
|
|
54
|
+
'Do NOT confirm with the user — record the classification so the orchestrating skill can confirm it at the human gate.',
|
|
55
|
+
{
|
|
56
|
+
label: 'classify-project',
|
|
57
|
+
phase: 'CLASSIFY',
|
|
58
|
+
schema: {
|
|
59
|
+
type: 'object',
|
|
60
|
+
required: ['repositoryType', 'parts'],
|
|
61
|
+
properties: {
|
|
62
|
+
repositoryType: { type: 'string', enum: ['monolith', 'monorepo', 'multi-part'] },
|
|
63
|
+
primaryLanguage: { type: 'string' },
|
|
64
|
+
parts: {
|
|
65
|
+
type: 'array',
|
|
66
|
+
items: {
|
|
67
|
+
type: 'object',
|
|
68
|
+
required: ['partId', 'rootPath', 'projectTypeId'],
|
|
69
|
+
properties: {
|
|
70
|
+
partId: { type: 'string' },
|
|
71
|
+
rootPath: { type: 'string' },
|
|
72
|
+
projectTypeId: { type: 'string' },
|
|
73
|
+
displayName: { type: 'string' }
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
classificationSummary: { type: 'string' }
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
const parts = (classification && classification.parts) || []
|
|
84
|
+
const isMultiPart = classification.repositoryType !== 'monolith' && parts.length > 1
|
|
85
|
+
log('classified as ' + classification.repositoryType + ' with ' + parts.length + ' part(s)')
|
|
86
|
+
|
|
87
|
+
// STEP 2 — Discover existing documentation and gather user context.
|
|
88
|
+
phase('EXISTING_DOCS')
|
|
89
|
+
const existingDocs = await agent(
|
|
90
|
+
'STEP 2 (document-project): for each part of this ' + classification.repositoryType + ' project, ' +
|
|
91
|
+
'inventory existing documentation. Scan for README.*, CONTRIBUTING.*, ARCHITECTURE.*, DEPLOYMENT.*/DEPLOY.*, ' +
|
|
92
|
+
'API.*, and anything under docs/, documentation/, .github/. ' +
|
|
93
|
+
'For each doc record: file path, doc type (readme | architecture | api | deployment | contributing | other), ' +
|
|
94
|
+
'and the owning part id when multi-part. ' +
|
|
95
|
+
'Do NOT ask the user for extra focus areas — that is a human gate; just return the inventory.',
|
|
96
|
+
{
|
|
97
|
+
label: 'existing-docs',
|
|
98
|
+
phase: 'EXISTING_DOCS',
|
|
99
|
+
schema: {
|
|
100
|
+
type: 'object',
|
|
101
|
+
required: ['docs'],
|
|
102
|
+
properties: {
|
|
103
|
+
docs: {
|
|
104
|
+
type: 'array',
|
|
105
|
+
items: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
required: ['path', 'docType'],
|
|
108
|
+
properties: {
|
|
109
|
+
path: { type: 'string' },
|
|
110
|
+
docType: { type: 'string' },
|
|
111
|
+
partId: { type: 'string' }
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
count: { type: 'number' }
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
)
|
|
120
|
+
log('found ' + ((existingDocs && existingDocs.docs && existingDocs.docs.length) || 0) + ' existing doc(s)')
|
|
121
|
+
|
|
122
|
+
// STEP 3 — Analyze technology stack for each part.
|
|
123
|
+
phase('TECH_STACK')
|
|
124
|
+
const techStack = await agent(
|
|
125
|
+
'STEP 3 (document-project): analyze the technology stack for each part. ' +
|
|
126
|
+
'Parse manifest files (package.json, go.mod, requirements.txt, Cargo.toml, etc.) per part to extract ' +
|
|
127
|
+
'framework, language, version, database, and key dependencies. Build a technology table ' +
|
|
128
|
+
'(Category, Technology, Version, Justification). Determine the architecture pattern per part, driven by its ' +
|
|
129
|
+
'project_type_id and framework (e.g. web -> layered/component-based, backend -> service/API-centric, ' +
|
|
130
|
+
'React -> component hierarchy, Express -> middleware pipeline). ' +
|
|
131
|
+
'Parts to cover: ' + JSON.stringify(parts.map(p => p.partId)) + '.',
|
|
132
|
+
{
|
|
133
|
+
label: 'tech-stack',
|
|
134
|
+
phase: 'TECH_STACK',
|
|
135
|
+
schema: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
required: ['perPart'],
|
|
138
|
+
properties: {
|
|
139
|
+
perPart: {
|
|
140
|
+
type: 'array',
|
|
141
|
+
items: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
required: ['partId', 'framework', 'language'],
|
|
144
|
+
properties: {
|
|
145
|
+
partId: { type: 'string' },
|
|
146
|
+
framework: { type: 'string' },
|
|
147
|
+
language: { type: 'string' },
|
|
148
|
+
database: { type: 'string' },
|
|
149
|
+
architecturePattern: { type: 'string' },
|
|
150
|
+
dependencies: { type: 'array', items: { type: 'string' } }
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
// STEP 4 — Conditional analysis driven by documentation_requirements boolean flags.
|
|
160
|
+
// scan_level gates depth: quick = pattern/glob only (no source reading); deep = read
|
|
161
|
+
// critical_directories; exhaustive = read all source (excl node_modules/.git/dist/build/coverage).
|
|
162
|
+
phase('CONDITIONAL_ANALYSIS')
|
|
163
|
+
const conditional = await agent(
|
|
164
|
+
'STEP 4 (document-project): perform conditional analysis per part, honoring scan_level="' + scanLevel + '". ' +
|
|
165
|
+
'For scan_level=quick use glob/grep and config/filename patterns ONLY — do NOT open source files. ' +
|
|
166
|
+
'For scan_level=deep read files in the critical_directories for each part type. ' +
|
|
167
|
+
'For scan_level=exhaustive read all source files (exclude node_modules, .git, dist, build, coverage), ' +
|
|
168
|
+
'processing one subfolder at a time. ' +
|
|
169
|
+
'For each part, consult its documentation_requirements row (from "' + reqCsv + '") and run the scans whose flag is true: ' +
|
|
170
|
+
'requires_api_scan -> catalog API routes/endpoints (controllers/ routes/ api/ handlers/), write api-contracts-{part}.md; ' +
|
|
171
|
+
'requires_data_models -> catalog schemas/entities/migrations (models/ schemas/ prisma/), write data-models-{part}.md; ' +
|
|
172
|
+
'requires_state_management -> identify stores/reducers/actions; ' +
|
|
173
|
+
'requires_ui_components -> inventory components/ ui/ widgets/ views/; ' +
|
|
174
|
+
'requires_hardware_docs -> note hardware/pinout/schematic references; ' +
|
|
175
|
+
'requires_asset_inventory -> catalog images/audio/models/sprites/textures. ' +
|
|
176
|
+
'Also scan supplementary patterns: config, auth/security, entry points, shared code, async/events, ci/cd, localization. ' +
|
|
177
|
+
'Return counts and the artifact filenames you would write.',
|
|
178
|
+
{
|
|
179
|
+
label: 'conditional-analysis',
|
|
180
|
+
phase: 'CONDITIONAL_ANALYSIS',
|
|
181
|
+
schema: {
|
|
182
|
+
type: 'object',
|
|
183
|
+
required: ['perPart'],
|
|
184
|
+
properties: {
|
|
185
|
+
perPart: {
|
|
186
|
+
type: 'array',
|
|
187
|
+
items: {
|
|
188
|
+
type: 'object',
|
|
189
|
+
required: ['partId'],
|
|
190
|
+
properties: {
|
|
191
|
+
partId: { type: 'string' },
|
|
192
|
+
apiEndpointCount: { type: 'number' },
|
|
193
|
+
dataModelCount: { type: 'number' },
|
|
194
|
+
uiComponentCount: { type: 'number' },
|
|
195
|
+
hasStateManagement: { type: 'boolean' },
|
|
196
|
+
hasAssets: { type: 'boolean' },
|
|
197
|
+
filesGenerated: { type: 'array', items: { type: 'string' } }
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
// STEP 5 — Source tree analysis with annotations.
|
|
207
|
+
phase('SOURCE_TREE')
|
|
208
|
+
const sourceTree = await agent(
|
|
209
|
+
'STEP 5 (document-project): generate an annotated source tree per part using each part\'s critical_directories. ' +
|
|
210
|
+
'Annotate the purpose of each critical directory, mark entry points, highlight key file locations, and ' +
|
|
211
|
+
'(for multi-part projects) note integration/interface points between parts. ' +
|
|
212
|
+
'Produce the content for source-tree-analysis.md.',
|
|
213
|
+
{
|
|
214
|
+
label: 'source-tree',
|
|
215
|
+
phase: 'SOURCE_TREE',
|
|
216
|
+
schema: {
|
|
217
|
+
type: 'object',
|
|
218
|
+
required: ['outputFile'],
|
|
219
|
+
properties: {
|
|
220
|
+
outputFile: { type: 'string' },
|
|
221
|
+
criticalFolderCount: { type: 'number' },
|
|
222
|
+
treeMarkdown: { type: 'string' }
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
// STEP 6 — Extract development and operational information.
|
|
229
|
+
phase('DEV_OPS')
|
|
230
|
+
const devOps = await agent(
|
|
231
|
+
'STEP 6 (document-project): extract development and operational info. ' +
|
|
232
|
+
'From manifests, configs, and existing docs determine: prerequisites (runtime versions), install steps, ' +
|
|
233
|
+
'environment setup (.env/config), build commands, run commands, and test commands (using test_file_patterns). ' +
|
|
234
|
+
'Detect deployment config via ci/cd patterns: Dockerfile, docker-compose.yml, k8s/, helm/, .github/workflows/, ' +
|
|
235
|
+
'.gitlab-ci.yml, deployment scripts, terraform/, pulumi/. ' +
|
|
236
|
+
'If a CONTRIBUTING file exists, extract code style, PR process, commit conventions, testing requirements. ' +
|
|
237
|
+
'Report which of dev-setup / deployment / contribution were found.',
|
|
238
|
+
{
|
|
239
|
+
label: 'dev-ops',
|
|
240
|
+
phase: 'DEV_OPS',
|
|
241
|
+
schema: {
|
|
242
|
+
type: 'object',
|
|
243
|
+
required: ['devSetupDocumented'],
|
|
244
|
+
properties: {
|
|
245
|
+
devSetupDocumented: { type: 'boolean' },
|
|
246
|
+
deploymentFound: { type: 'boolean' },
|
|
247
|
+
contributionFound: { type: 'boolean' },
|
|
248
|
+
notes: { type: 'string' }
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
// STEP 7 — Multi-part integration architecture (only when project has multiple parts).
|
|
255
|
+
phase('INTEGRATION')
|
|
256
|
+
let integration = { applicable: false, integrationPoints: [] }
|
|
257
|
+
if (isMultiPart) {
|
|
258
|
+
integration = await agent(
|
|
259
|
+
'STEP 7 (document-project): this is a multi-part project. Analyze how parts communicate ' +
|
|
260
|
+
'(REST, GraphQL, gRPC, message queues, shared databases) via integration_scan_patterns. ' +
|
|
261
|
+
'Build an integration_points list of { from, to, type, details } and produce integration-architecture.md content.',
|
|
262
|
+
{
|
|
263
|
+
label: 'integration-architecture',
|
|
264
|
+
phase: 'INTEGRATION',
|
|
265
|
+
schema: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
required: ['integrationPoints'],
|
|
268
|
+
properties: {
|
|
269
|
+
integrationPoints: {
|
|
270
|
+
type: 'array',
|
|
271
|
+
items: {
|
|
272
|
+
type: 'object',
|
|
273
|
+
required: ['from', 'to', 'type'],
|
|
274
|
+
properties: {
|
|
275
|
+
from: { type: 'string' },
|
|
276
|
+
to: { type: 'string' },
|
|
277
|
+
type: { type: 'string' },
|
|
278
|
+
details: { type: 'string' }
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
outputFile: { type: 'string' }
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
)
|
|
287
|
+
integration.applicable = true
|
|
288
|
+
} else {
|
|
289
|
+
log('single-part project — integration architecture step skipped (mirrors source if-guard)')
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// STEP 8 — Generate architecture documentation for each part.
|
|
293
|
+
phase('ARCHITECTURE')
|
|
294
|
+
const architecture = await agent(
|
|
295
|
+
'STEP 8 (document-project): generate architecture documentation per part. For each part fill: ' +
|
|
296
|
+
'Executive Summary, Technology Stack (Step 3), Architecture Pattern, Data Architecture (Step 4 data models), ' +
|
|
297
|
+
'API Design (Step 4 APIs if applicable), Component Overview (Step 4 components if applicable), Source Tree (Step 5), ' +
|
|
298
|
+
'Development Workflow + Deployment Architecture (Step 6), Testing Strategy. ' +
|
|
299
|
+
'For a single-part project write architecture.md; for multi-part write architecture-{part}.md per part. ' +
|
|
300
|
+
'No placeholder sections — every section must be filled from discovered data. Report the files you would write.',
|
|
301
|
+
{
|
|
302
|
+
label: 'architecture-docs',
|
|
303
|
+
phase: 'ARCHITECTURE',
|
|
304
|
+
schema: {
|
|
305
|
+
type: 'object',
|
|
306
|
+
required: ['files'],
|
|
307
|
+
properties: {
|
|
308
|
+
files: { type: 'array', items: { type: 'string' } }
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
// STEP 9 — Generate supporting documentation files.
|
|
315
|
+
phase('SUPPORTING_DOCS')
|
|
316
|
+
const supporting = await agent(
|
|
317
|
+
'STEP 9 (document-project): generate the supporting docs from the discovered data. Always: ' +
|
|
318
|
+
'project-overview.md (name, purpose, exec summary, tech stack table, architecture type, repo structure, links) ' +
|
|
319
|
+
'and source-tree-analysis.md. Conditionally: component-inventory(-{part}).md if UI components exist; ' +
|
|
320
|
+
'development-guide(-{part}).md; deployment-guide.md if deployment config found; contribution-guide.md if guidelines found; ' +
|
|
321
|
+
'api-contracts(-{part}).md if APIs documented; data-models(-{part}).md if data models found; ' +
|
|
322
|
+
'and for multi-part: integration-architecture.md + project-parts.json metadata. ' +
|
|
323
|
+
'Use the (To be generated) marker exactly for any doc that should exist but cannot be produced. ' +
|
|
324
|
+
'Return the full list of files written.',
|
|
325
|
+
{
|
|
326
|
+
label: 'supporting-docs',
|
|
327
|
+
phase: 'SUPPORTING_DOCS',
|
|
328
|
+
schema: {
|
|
329
|
+
type: 'object',
|
|
330
|
+
required: ['files'],
|
|
331
|
+
properties: {
|
|
332
|
+
files: { type: 'array', items: { type: 'string' } },
|
|
333
|
+
toBeGeneratedMarkers: { type: 'array', items: { type: 'string' } }
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
// STEP 10 — Generate master index as the primary AI retrieval source.
|
|
340
|
+
phase('MASTER_INDEX')
|
|
341
|
+
const masterIndex = await agent(
|
|
342
|
+
'STEP 10 (document-project): write index.md — the master entry point for AI-assisted development. ' +
|
|
343
|
+
'Include: Project Overview (type/repository_type, primary language, architecture), Quick Reference ' +
|
|
344
|
+
'(single-part: tech stack/entry point/pattern; multi-part: per-part block), Generated Documentation links, ' +
|
|
345
|
+
'Existing Documentation links, and a Getting Started section. ' +
|
|
346
|
+
'Before linking each generated doc, check whether the file actually exists; if a doc was expected but missing, ' +
|
|
347
|
+
'append the EXACT marker "(To be generated)" to its link so Step 11 can detect it. ' +
|
|
348
|
+
'Return the missing-doc list captured from those markers.',
|
|
349
|
+
{
|
|
350
|
+
label: 'master-index',
|
|
351
|
+
phase: 'MASTER_INDEX',
|
|
352
|
+
schema: {
|
|
353
|
+
type: 'object',
|
|
354
|
+
required: ['outputFile'],
|
|
355
|
+
properties: {
|
|
356
|
+
outputFile: { type: 'string' },
|
|
357
|
+
missingDocs: { type: 'array', items: { type: 'string' } }
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
)
|
|
362
|
+
|
|
363
|
+
// STEP 11 — Validate against checklist + detect incomplete-documentation markers.
|
|
364
|
+
// The source loops here at a HUMAN gate (generate-incomplete / review / finalize). The
|
|
365
|
+
// script does NOT loop or prompt: it runs the validation + marker scan once and returns a
|
|
366
|
+
// verdict; the orchestrating skill drives the iteration at the gate.
|
|
367
|
+
phase('VALIDATE')
|
|
368
|
+
const validation = await agent(
|
|
369
|
+
'STEP 11 (document-project): validate the generated documentation set against the checklist ' +
|
|
370
|
+
'(checklist.md): write-as-you-go coverage, classification accuracy, tech-stack completeness, file completeness, ' +
|
|
371
|
+
'index navigation, brownfield-PRD readiness. ' +
|
|
372
|
+
'Then scan index.md for incomplete-documentation markers: PRIMARY exact "(To be generated)", FALLBACK fuzzy ' +
|
|
373
|
+
'"(TBD)" "(TODO)" "(Coming soon)" "(Not yet generated)" "(Pending)". ' +
|
|
374
|
+
'For each marker capture { title, filePath, docType, partId, fuzzyMatch }. ' +
|
|
375
|
+
'Do NOT prompt the user to generate/review/finalize — return a verdict the orchestrating skill presents at the gate.',
|
|
376
|
+
{
|
|
377
|
+
label: 'validate-docs',
|
|
378
|
+
phase: 'VALIDATE',
|
|
379
|
+
schema: {
|
|
380
|
+
type: 'object',
|
|
381
|
+
required: ['checklistPassed', 'incompleteDocs'],
|
|
382
|
+
properties: {
|
|
383
|
+
checklistPassed: { type: 'boolean' },
|
|
384
|
+
criticalIssues: { type: 'array', items: { type: 'string' } },
|
|
385
|
+
incompleteDocs: {
|
|
386
|
+
type: 'array',
|
|
387
|
+
items: {
|
|
388
|
+
type: 'object',
|
|
389
|
+
required: ['title', 'docType'],
|
|
390
|
+
properties: {
|
|
391
|
+
title: { type: 'string' },
|
|
392
|
+
filePath: { type: 'string' },
|
|
393
|
+
docType: { type: 'string' },
|
|
394
|
+
partId: { type: 'string' },
|
|
395
|
+
fuzzyMatch: { type: 'boolean' }
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
// STEP 12 — Finalize and compile the summary report.
|
|
405
|
+
phase('FINALIZE')
|
|
406
|
+
const finalize = await agent(
|
|
407
|
+
'STEP 12 (document-project): compile the final summary. List every generated file under "' + outputFolder + '" ' +
|
|
408
|
+
'with the master index (index.md) flagged as the primary entry point. Compile the verification recap: ' +
|
|
409
|
+
'verificationSummary (concrete tests/extractions executed, or "none run"), openRisks (remaining risks/TODOs, or "none"), ' +
|
|
410
|
+
'nextChecks (recommended actions before a brownfield PRD / PR, or "none"). ' +
|
|
411
|
+
'Run date provided by orchestrator: ' + runDate + '.',
|
|
412
|
+
{
|
|
413
|
+
label: 'finalize',
|
|
414
|
+
phase: 'FINALIZE',
|
|
415
|
+
schema: {
|
|
416
|
+
type: 'object',
|
|
417
|
+
required: ['generatedFiles'],
|
|
418
|
+
properties: {
|
|
419
|
+
generatedFiles: { type: 'array', items: { type: 'string' } },
|
|
420
|
+
verificationSummary: { type: 'string' },
|
|
421
|
+
openRisks: { type: 'string' },
|
|
422
|
+
nextChecks: { type: 'string' }
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
const incompleteCount = (validation && validation.incompleteDocs && validation.incompleteDocs.length) || 0
|
|
429
|
+
|
|
430
|
+
// The human decision (review-iteration loop / finalize) lives OUTSIDE the script.
|
|
431
|
+
// We return a structured verdict; needsHumanGate is true so the orchestrating skill can
|
|
432
|
+
// present classification confirmation + the incomplete-docs menu and record FD/strict state.
|
|
433
|
+
return {
|
|
434
|
+
workflow: 'document-project',
|
|
435
|
+
summary: 'Documented ' + classification.repositoryType + ' project (' + parts.length +
|
|
436
|
+
' part(s)) at scan_level=' + scanLevel + '; ' + incompleteCount + ' incomplete-doc marker(s) detected.',
|
|
437
|
+
steps: 12,
|
|
438
|
+
mode: workflowMode,
|
|
439
|
+
scanLevel: scanLevel,
|
|
440
|
+
needsHumanGate: true,
|
|
441
|
+
classification: classification,
|
|
442
|
+
result: {
|
|
443
|
+
existingDocs: existingDocs,
|
|
444
|
+
techStack: techStack,
|
|
445
|
+
conditionalAnalysis: conditional,
|
|
446
|
+
sourceTree: sourceTree,
|
|
447
|
+
devOps: devOps,
|
|
448
|
+
integration: integration,
|
|
449
|
+
architecture: architecture,
|
|
450
|
+
supportingDocs: supporting,
|
|
451
|
+
masterIndex: masterIndex,
|
|
452
|
+
validation: validation,
|
|
453
|
+
finalize: finalize
|
|
454
|
+
}
|
|
455
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
name: 'qa-automate',
|
|
3
|
+
description: 'Native port of the BYAN qa-automate (Quinn QA) workflow: autonomously generate automated API + E2E tests for already-implemented code using the project\'s existing test framework, run them to green (bounded fix loop), and return a structured test-automation summary for the orchestrating skill to present at the human gate.',
|
|
4
|
+
phases: [
|
|
5
|
+
{ title: 'DETECT', detail: 'Step 0 - detect the project test framework' },
|
|
6
|
+
{ title: 'IDENTIFY', detail: 'Step 1 - identify the features/dirs to test' },
|
|
7
|
+
{ title: 'API_TESTS', detail: 'Step 2 - generate API tests (if applicable)' },
|
|
8
|
+
{ title: 'E2E_TESTS', detail: 'Step 3 - generate E2E tests (if UI exists)' },
|
|
9
|
+
{ title: 'RUN', detail: 'Step 4 - run tests, fix failures immediately (bounded loop)' },
|
|
10
|
+
{ title: 'SUMMARY', detail: 'Step 5 - produce the test-automation summary verdict' },
|
|
11
|
+
],
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// FD / STRICT STATE CONTRACT (re-asserted inline — enforcement-bridge).
|
|
16
|
+
//
|
|
17
|
+
// The in-CLI Workflow tool runs this script OUTSIDE the conversation turn, so
|
|
18
|
+
// BYAN's main-thread hooks (fd-phase-guard, strict-scope-guard, strict-stop-
|
|
19
|
+
// guard, mantra-validate) DO NOT fire here. This script therefore:
|
|
20
|
+
// - NEVER imports/requires _byan/.../lib/fd-state.js and NEVER writes
|
|
21
|
+
// fd-state.json directly (enforced by byan-lint-workflows.js).
|
|
22
|
+
// - uses NO wall-clock and NO randomness primitive (Date/RNG break
|
|
23
|
+
// resume); any date/id must arrive via args.
|
|
24
|
+
// - returns DATA only. The orchestrating skill is the human-gated conductor;
|
|
25
|
+
// IT records FD/strict state via the byan_fd_* / byan_strict_* MCP tools
|
|
26
|
+
// AT the gate. The generated test FILES + the summary markdown are the
|
|
27
|
+
// workflow's product, not BYAN platform state.
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
// Convergence guard for Step 4 ("Run Tests ... If failures occur, fix them
|
|
31
|
+
// immediately"). The source prose has no explicit cap, but an autonomous fix
|
|
32
|
+
// loop must be bounded so the model cannot spin forever in the sandbox. Hard
|
|
33
|
+
// cap = 3 fix attempts; turns the prose into a real JS counter.
|
|
34
|
+
const MAX_FIX_ATTEMPTS = 3
|
|
35
|
+
|
|
36
|
+
const RUN_SCHEMA = {
|
|
37
|
+
type: 'object',
|
|
38
|
+
required: ['pass', 'fixed'],
|
|
39
|
+
properties: {
|
|
40
|
+
pass: { type: 'boolean', description: 'true ONLY if the full generated test suite passes via the project test command' },
|
|
41
|
+
fixed: { type: 'boolean', description: 'true if this attempt changed test/source code to address failures' },
|
|
42
|
+
failures: { type: 'array', items: { type: 'string' }, description: 'precise failing tests / errors when not passing' },
|
|
43
|
+
testCommand: { type: 'string', description: 'the exact command used to run the tests' },
|
|
44
|
+
summary: { type: 'string', description: 'one-line run status' },
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const SUMMARY_SCHEMA = {
|
|
49
|
+
type: 'object',
|
|
50
|
+
required: ['ok', 'apiTestFiles', 'e2eTestFiles', 'allPass'],
|
|
51
|
+
properties: {
|
|
52
|
+
ok: { type: 'boolean', description: 'true if a complete, faithful test-automation summary was produced' },
|
|
53
|
+
apiTestFiles: { type: 'array', items: { type: 'string' }, description: 'relative paths of generated API test files' },
|
|
54
|
+
e2eTestFiles: { type: 'array', items: { type: 'string' }, description: 'relative paths of generated E2E test files' },
|
|
55
|
+
allPass: { type: 'boolean', description: 'true if all generated tests pass' },
|
|
56
|
+
apiCoverage: { type: 'string', description: 'e.g. "5/10 endpoints covered"' },
|
|
57
|
+
uiCoverage: { type: 'string', description: 'e.g. "3/8 UI features covered"' },
|
|
58
|
+
summaryPath: { type: 'string', description: 'path the test-summary.md was written to (implementation_artifacts/tests/test-summary.md)' },
|
|
59
|
+
notes: { type: 'string', description: 'next steps / caveats' },
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Inputs arrive via args (no clock/RNG inside the script).
|
|
64
|
+
const target = (args && args.target) || 'auto-discover features in the codebase'
|
|
65
|
+
const testDir = (args && args.testDir) || '{project-root}/tests'
|
|
66
|
+
const sourceDir = (args && args.sourceDir) || '{project-root}'
|
|
67
|
+
const summaryPath = (args && args.summaryPath) || '{implementation_artifacts}/tests/test-summary.md'
|
|
68
|
+
|
|
69
|
+
// --- Step 0: Detect Test Framework -----------------------------------------
|
|
70
|
+
phase('DETECT')
|
|
71
|
+
const framework = await agent(
|
|
72
|
+
`You are Quinn (BYAN QA), running qa-automate Step 0 (Detect Test Framework). ` +
|
|
73
|
+
`Scope: GENERATE TESTS ONLY — do NOT do code review or story validation. ` +
|
|
74
|
+
`Inspect the project under ${JSON.stringify(sourceDir)}: read package.json dependencies for an existing test ` +
|
|
75
|
+
`framework (playwright, jest, vitest, cypress, etc.) and read existing test files to learn the project's patterns. ` +
|
|
76
|
+
`Use whatever framework the project already has. If NO framework exists: analyze the source to determine the project ` +
|
|
77
|
+
`type (React, Vue, Node API, ...), determine the currently recommended test framework for that stack, and propose it ` +
|
|
78
|
+
`(flag that user confirmation is normally required). Report: detected framework, version, config file, conventions, ` +
|
|
79
|
+
`and the test command. State explicitly if none was found.`,
|
|
80
|
+
{ label: 'detect-framework', phase: 'DETECT' }
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
// --- Step 1: Identify Features ----------------------------------------------
|
|
84
|
+
phase('IDENTIFY')
|
|
85
|
+
const features = await agent(
|
|
86
|
+
`qa-automate Step 1 (Identify Features). Target requested: ${JSON.stringify(target)}. ` +
|
|
87
|
+
`Framework context: ${framework}\n` +
|
|
88
|
+
`Determine exactly what to test: a specific feature/component, a directory to scan (e.g. src/components/), ` +
|
|
89
|
+
`or auto-discover features across ${JSON.stringify(sourceDir)}. Classify each into API surfaces (endpoints/services) ` +
|
|
90
|
+
`vs UI surfaces (components/pages with user interaction). Report the concrete list of testable units with their files.`,
|
|
91
|
+
{ label: 'identify-features', phase: 'IDENTIFY' }
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
// --- Step 2: Generate API Tests (if applicable) -----------------------------
|
|
95
|
+
phase('API_TESTS')
|
|
96
|
+
const apiTests = await agent(
|
|
97
|
+
`qa-automate Step 2 (Generate API Tests, if applicable). Framework: ${framework}\nFeatures: ${features}\n` +
|
|
98
|
+
`For each API endpoint/service identified, generate tests using the project's existing framework patterns that: ` +
|
|
99
|
+
`test status codes (200/400/404/500), validate response structure, and cover the happy path + 1-2 error cases. ` +
|
|
100
|
+
`Write them under ${JSON.stringify(testDir)} (e.g. tests/api/...). Keep them simple — standard framework APIs, no ` +
|
|
101
|
+
`complex fixture composition, no over-engineering. If there are no API surfaces, skip and say so. ` +
|
|
102
|
+
`Report the generated API test file paths.`,
|
|
103
|
+
{ label: 'generate-api-tests', phase: 'API_TESTS' }
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
// --- Step 3: Generate E2E Tests (if UI exists) ------------------------------
|
|
107
|
+
phase('E2E_TESTS')
|
|
108
|
+
const e2eTests = await agent(
|
|
109
|
+
`qa-automate Step 3 (Generate E2E Tests, if UI exists). Framework: ${framework}\nFeatures: ${features}\n` +
|
|
110
|
+
`For each UI feature identified, generate end-to-end tests that: cover user workflows end-to-end, use semantic ` +
|
|
111
|
+
`locators (roles, labels, text — not brittle CSS), focus on real user interactions (clicks, form fills, navigation), ` +
|
|
112
|
+
`assert visible outcomes, and stay linear and simple. Follow the project's existing test patterns. ` +
|
|
113
|
+
`Forbid hardcoded waits/sleeps; tests must be independent (no order dependency). Write them under ` +
|
|
114
|
+
`${JSON.stringify(testDir)} (e.g. tests/e2e/...). If there is no UI, skip and say so. Report the generated E2E test file paths.`,
|
|
115
|
+
{ label: 'generate-e2e-tests', phase: 'E2E_TESTS' }
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
// --- Step 4: Run Tests (with immediate-fix, bounded loop) -------------------
|
|
119
|
+
phase('RUN')
|
|
120
|
+
// "Execute tests to verify they pass. If failures occur, fix them immediately."
|
|
121
|
+
// Bounded so an autonomous fix loop cannot spin forever.
|
|
122
|
+
let attempts = 0
|
|
123
|
+
let run = { pass: false, fixed: false, failures: ['not started'] }
|
|
124
|
+
while (true) {
|
|
125
|
+
attempts += 1
|
|
126
|
+
run = await agent(
|
|
127
|
+
`qa-automate Step 4 (Run Tests), attempt ${attempts}/${MAX_FIX_ATTEMPTS}. Framework: ${framework}\n` +
|
|
128
|
+
`Generated API tests: ${apiTests}\nGenerated E2E tests: ${e2eTests}\n` +
|
|
129
|
+
`Run the generated tests using the project's test command. Set pass=true ONLY if the full generated suite passes. ` +
|
|
130
|
+
`If failures occur, FIX them immediately (correct the tests or the obviously-broken test setup — do NOT silently ` +
|
|
131
|
+
`delete tests to make them pass) and report fixed=true with the precise failures addressed. ` +
|
|
132
|
+
`Report the exact test command used.`,
|
|
133
|
+
{ label: `run-tests-attempt-${attempts}`, phase: 'RUN', schema: RUN_SCHEMA }
|
|
134
|
+
)
|
|
135
|
+
log(`run attempt ${attempts}: pass=${Boolean(run && run.pass)} fixed=${Boolean(run && run.fixed)}`)
|
|
136
|
+
if (run && run.pass) break
|
|
137
|
+
if (attempts >= MAX_FIX_ATTEMPTS) break
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// --- Step 5: Create Summary -------------------------------------------------
|
|
141
|
+
phase('SUMMARY')
|
|
142
|
+
const summary = await agent(
|
|
143
|
+
`qa-automate Step 5 (Create Summary). Produce the Test Automation Summary markdown and save it to ` +
|
|
144
|
+
`${JSON.stringify(summaryPath)}. Use the source template sections: Generated Tests (API Tests, E2E Tests with ` +
|
|
145
|
+
`checkboxes + one-line description per file), Coverage (API endpoints X/Y, UI features X/Y), and Next Steps.\n` +
|
|
146
|
+
`Inputs — API tests: ${apiTests}\nE2E tests: ${e2eTests}\nRun result: ${JSON.stringify(run)}\n` +
|
|
147
|
+
`Be faithful: list only files actually generated; do not over-state coverage. Return the structured fields.`,
|
|
148
|
+
{ label: 'create-summary', phase: 'SUMMARY', schema: SUMMARY_SCHEMA }
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
// Return DATA only. The orchestrating skill presents this at the human gate
|
|
152
|
+
// and records FD/strict state via MCP.
|
|
153
|
+
return {
|
|
154
|
+
workflow: 'qa-automate',
|
|
155
|
+
target,
|
|
156
|
+
framework,
|
|
157
|
+
steps: 6,
|
|
158
|
+
apiTestFiles: (summary && summary.apiTestFiles) || [],
|
|
159
|
+
e2eTestFiles: (summary && summary.e2eTestFiles) || [],
|
|
160
|
+
allTestsPass: Boolean(run && run.pass),
|
|
161
|
+
fixAttempts: attempts,
|
|
162
|
+
maxFixAttempts: MAX_FIX_ATTEMPTS,
|
|
163
|
+
failures: (run && run.failures) || [],
|
|
164
|
+
apiCoverage: (summary && summary.apiCoverage) || 'unknown',
|
|
165
|
+
uiCoverage: (summary && summary.uiCoverage) || 'unknown',
|
|
166
|
+
summaryPath: (summary && summary.summaryPath) || summaryPath,
|
|
167
|
+
notes: (summary && summary.notes) || '',
|
|
168
|
+
needsHumanGate: true,
|
|
169
|
+
}
|