bmad-plus 0.13.0 → 0.16.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 +74 -0
- package/README.md +113 -487
- package/SECURITY.md +71 -0
- package/THIRD-PARTY-LICENSES.md +349 -0
- package/osint-agent-package/README.md +1 -1
- package/package.json +14 -3
- package/readme-international/README.de.md +18 -8
- package/readme-international/README.es.md +19 -9
- package/readme-international/README.fr.md +18 -8
- package/src/bmad-plus/agents/agent-architect-dev/SKILL.md +11 -13
- package/src/bmad-plus/agents/agent-orchestrator/SKILL.md +148 -9
- package/src/bmad-plus/agents/agent-quality/SKILL.md +41 -11
- package/src/bmad-plus/data/role-triggers.yaml +19 -0
- package/src/bmad-plus/module-help.csv +1 -0
- package/src/bmad-plus/module.yaml +1 -0
- package/src/bmad-plus/packs/pack-dev-studio/README.md +133 -141
- package/src/bmad-plus/packs/pack-dev-studio/SKILL.md +49 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/analyst-agent.md +35 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/document-project.md +59 -59
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/domain-research.md +55 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/market-research.md +58 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/prfaq.md +55 -132
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/product-brief.md +63 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/tech-writer-agent.md +54 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/analysis/technical-research.md +54 -93
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/architect-agent.md +32 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-architecture.md +67 -71
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/create-epics-stories.md +61 -90
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/generate-project-context.md +56 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/implementation-readiness.md +55 -88
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01-init.md +20 -153
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-01b-continue.md +20 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-02-context.md +14 -220
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-03-starter.md +20 -329
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-04-decisions.md +15 -314
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-05-patterns.md +15 -355
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-06-structure.md +15 -375
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-07-validation.md +14 -357
- package/src/bmad-plus/packs/pack-dev-studio/categories/architecture/steps/step-08-complete.md +13 -78
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/checkpoint-preview.md +52 -65
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-01-gather-context.md +14 -81
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-02-review.md +14 -31
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-03-triage.md +14 -45
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review-steps/step-04-present.md +13 -128
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/code-review.md +61 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/correct-course.md +55 -298
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/create-story.md +54 -426
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-agent.md +48 -69
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story-checklist.md +24 -80
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/dev-story.md +64 -482
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/investigate.md +50 -184
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/qa-e2e-tests.md +57 -173
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/quick-dev.md +56 -108
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/retrospective.md +54 -1509
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-planning.md +54 -296
- package/src/bmad-plus/packs/pack-dev-studio/categories/implementation/sprint-status.md +41 -283
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-prd.md +58 -18
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/create-ux-design.md +103 -72
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/edit-prd.md +55 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/pm-agent.md +34 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/prd.md +46 -87
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/steps/step-01-init.md +10 -0
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/ux-designer-agent.md +30 -60
- package/src/bmad-plus/packs/pack-dev-studio/categories/planning/validate-prd.md +57 -27
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/advanced-elicitation.md +47 -138
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/adversarial-review.md +48 -34
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/bmad-help.md +51 -68
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/brainstorming.md +46 -3
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/customize.md +68 -109
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/distillator.md +53 -174
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/edge-case-hunter.md +39 -53
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-prose.md +45 -83
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/editorial-review-structure.md +45 -176
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/index-docs.md +45 -63
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/party-mode.md +53 -124
- package/src/bmad-plus/packs/pack-dev-studio/categories/utilities/shard-doc.md +44 -100
- package/src/bmad-plus/packs/pack-dev-studio/dev-studio-orchestrator.md +56 -116
- package/src/bmad-plus/packs/pack-dev-studio/shared/architecture-decision-template.md +49 -12
- package/src/bmad-plus/packs/pack-dev-studio/shared/bwml-spec.md +51 -328
- package/src/bmad-plus/packs/pack-dev-studio/shared/catalog.json +489 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/execution.md +69 -0
- package/src/bmad-plus/packs/pack-dev-studio/shared/module-help.csv +39 -32
- package/src/bmad-plus/packs/pack-dev-studio/upstream-sync.yaml +85 -14
- package/src/bmad-plus/packs/pack-memory/README.md +35 -4
- package/src/bmad-plus/packs/pack-memory/memory-orchestrator.md +33 -6
- package/src/bmad-plus/packs/pack-memory/shared/karpathy-guardrails.md +3 -3
- package/src/bmad-plus/packs/pack-memory/shared/memory-protocol.md +27 -3
- package/src/bmad-plus/packs/pack-memory/zecher-agent.md +18 -2
- package/src/bmad-plus/packs/pack-seo/SKILL.md +27 -1
- package/src/bmad-plus/packs/pack-seo/seo-chief.md +16 -1
- package/src/bmad-plus/packs/pack-seo/seo-judge.md +12 -0
- package/src/bmad-plus/packs/pack-seo/seo-scout.md +12 -0
- package/src/bmad-plus/skills/bmad-plus-autopilot/SKILL.md +49 -12
- package/src/bmad-plus/skills/bmad-plus-parallel/SKILL.md +17 -3
- package/src/bmad-plus/skills/bmad-plus-sync/SKILL.md +76 -65
- package/src/bmad-plus/skills/bmad-plus-uat/SKILL.md +144 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-results.schema.json +60 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-spec.schema.json +121 -0
- package/src/bmad-plus/skills/bmad-plus-uat/ref/uat-triage.schema.json +60 -0
- package/src/bmad-plus/skills/bmad-plus-uat/template/page.html +552 -0
- package/src/bmad-plus/skills/bmad-plus-uat/template/strings.json +362 -0
- package/src/bmad-plus/skills/dev-studio/SKILL.md +19 -0
- package/tools/build/check-install-contract.js +367 -17
- package/tools/build/generate.js +229 -40
- package/tools/build/generated-adapters/.codex/AGENTS.md +1 -1
- package/tools/build/generated-adapters/.cursor/rules/bmad-plus.mdc +1 -1
- package/tools/build/generated-adapters/.opencode/AGENTS.md +1 -1
- package/tools/build/generated-adapters/AGENTS.md +1 -1
- package/tools/build/generated-adapters/CLAUDE.md +1 -1
- package/tools/build/generated-adapters/CONVENTIONS.md +1 -1
- package/tools/build/generated-adapters/GEMINI.md +1 -1
- package/tools/build/pack-delivery.js +78 -0
- package/tools/cli/bmad-plus-cli.js +15 -12
- package/tools/cli/commands/doctor.js +50 -189
- package/tools/cli/commands/install.js +22 -3
- package/tools/cli/commands/memory-journal-cmd.js +119 -19
- package/tools/cli/commands/nexus.js +111 -0
- package/tools/cli/commands/studio.js +68 -0
- package/tools/cli/commands/uat.js +389 -0
- package/tools/cli/lib/README-memory-journal.md +19 -8
- package/tools/cli/lib/installation-health.js +366 -0
- package/tools/cli/lib/memory-journal.js +0 -0
- package/tools/cli/lib/memory-outcomes.js +293 -0
- package/tools/cli/lib/memory-store.js +139 -0
- package/tools/cli/lib/nexus-process.js +377 -0
- package/tools/cli/lib/nexus.js +1532 -0
- package/tools/cli/lib/pack-copy.js +39 -11
- package/tools/cli/lib/packs.js +134 -11
- package/tools/cli/lib/python-health.js +233 -0
- package/tools/cli/lib/python-provision.js +2 -2
- package/tools/cli/lib/studio.js +310 -0
- package/tools/cli/lib/uat.js +869 -0
- package/tools/maintain/upstream-candidate.js +456 -0
- package/tools/release/publication-content.js +903 -0
- package/tools/release/supply-chain.js +282 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/** Read-only Dev Studio resource resolution; execution belongs to the host. */
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const crypto = require('node:crypto');
|
|
7
|
+
const yaml = require('js-yaml');
|
|
8
|
+
const { safeTarget } = require('../../build/generate-adapters');
|
|
9
|
+
|
|
10
|
+
const PACK = '.agents/skills/pack-dev-studio';
|
|
11
|
+
const MAX_FILE = 256 * 1024;
|
|
12
|
+
const MAX_CONTEXT = 2 * 1024 * 1024;
|
|
13
|
+
const hash = (bytes) => crypto.createHash('sha256').update(bytes).digest('hex');
|
|
14
|
+
|
|
15
|
+
function relativePath(file) {
|
|
16
|
+
if (
|
|
17
|
+
typeof file !== 'string' ||
|
|
18
|
+
!file ||
|
|
19
|
+
/[\\:\0?#{}]/.test(file) ||
|
|
20
|
+
file.split('/').some((part) => !part || part === '.' || part === '..')
|
|
21
|
+
) {
|
|
22
|
+
throw new Error('Expected a portable relative file path: ' + String(file));
|
|
23
|
+
}
|
|
24
|
+
return file;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function target(root, file) {
|
|
28
|
+
relativePath(file);
|
|
29
|
+
const result = safeTarget(root, file);
|
|
30
|
+
let parent = path.resolve(root);
|
|
31
|
+
for (const part of file.split('/')) {
|
|
32
|
+
if (!fs.existsSync(parent)) break;
|
|
33
|
+
const names = fs.readdirSync(parent);
|
|
34
|
+
if (!names.includes(part) && names.some((name) => name.toLowerCase() === part.toLowerCase())) {
|
|
35
|
+
throw new Error('Path case mismatch: ' + file);
|
|
36
|
+
}
|
|
37
|
+
parent = path.join(parent, part);
|
|
38
|
+
}
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readText(root, file, limit = MAX_FILE) {
|
|
43
|
+
const resolved = target(root, file);
|
|
44
|
+
const stat = fs.statSync(resolved);
|
|
45
|
+
if (!stat.isFile() || stat.size > limit) throw new Error('Resource is not bounded text: ' + file);
|
|
46
|
+
const bytes = fs.readFileSync(resolved);
|
|
47
|
+
if (bytes.includes(0) || bytes.length > limit)
|
|
48
|
+
throw new Error('Resource is not bounded text: ' + file);
|
|
49
|
+
const content = bytes.toString('utf8');
|
|
50
|
+
if (!Buffer.from(content).equals(bytes)) throw new Error('Resource is not UTF-8: ' + file);
|
|
51
|
+
return { path: file, sha256: hash(bytes), content };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readJson(root, file) {
|
|
55
|
+
try {
|
|
56
|
+
return JSON.parse(readText(root, file).content);
|
|
57
|
+
} catch (error) {
|
|
58
|
+
throw new Error(file + ': ' + error.message, { cause: error });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function loadCatalog(packRoot) {
|
|
63
|
+
const catalog = readJson(packRoot, 'shared/catalog.json');
|
|
64
|
+
if (
|
|
65
|
+
catalog.schemaVersion !== 1 ||
|
|
66
|
+
!Array.isArray(catalog.agents) ||
|
|
67
|
+
!Array.isArray(catalog.workflows) ||
|
|
68
|
+
!Array.isArray(catalog.compatibility) ||
|
|
69
|
+
!catalog.agents.length ||
|
|
70
|
+
!catalog.workflows.length
|
|
71
|
+
) {
|
|
72
|
+
throw new Error('Unsupported Dev Studio catalog');
|
|
73
|
+
}
|
|
74
|
+
catalog.compatibility.forEach(relativePath);
|
|
75
|
+
const agents = new Set();
|
|
76
|
+
const routes = new Set();
|
|
77
|
+
const entries = new Set();
|
|
78
|
+
for (const agent of catalog.agents) {
|
|
79
|
+
if (
|
|
80
|
+
!agent ||
|
|
81
|
+
!/^[a-z][a-z0-9-]*$/.test(agent.id) ||
|
|
82
|
+
agents.has(agent.id) ||
|
|
83
|
+
typeof agent.persona !== 'string' ||
|
|
84
|
+
!agent.persona.trim()
|
|
85
|
+
)
|
|
86
|
+
throw new Error('Invalid or duplicate persona');
|
|
87
|
+
relativePath(agent.path);
|
|
88
|
+
if (entries.has(agent.path)) throw new Error('Duplicate catalog entry: ' + agent.path);
|
|
89
|
+
entries.add(agent.path);
|
|
90
|
+
agents.add(agent.id);
|
|
91
|
+
}
|
|
92
|
+
for (const workflow of catalog.workflows) {
|
|
93
|
+
if (
|
|
94
|
+
!workflow ||
|
|
95
|
+
!/^[a-z][a-z0-9-]*$/.test(workflow.id) ||
|
|
96
|
+
!agents.has(workflow.agent) ||
|
|
97
|
+
!['request', 'artifact', 'project', 'none'].includes(workflow.input) ||
|
|
98
|
+
!Array.isArray(workflow.aliases) ||
|
|
99
|
+
!Array.isArray(workflow.resources)
|
|
100
|
+
) {
|
|
101
|
+
throw new Error('Invalid workflow declaration');
|
|
102
|
+
}
|
|
103
|
+
relativePath(workflow.path);
|
|
104
|
+
if (entries.has(workflow.path)) throw new Error('Duplicate catalog entry: ' + workflow.path);
|
|
105
|
+
entries.add(workflow.path);
|
|
106
|
+
for (const resource of workflow.resources) relativePath(resource);
|
|
107
|
+
for (const route of [workflow.id, ...workflow.aliases]) {
|
|
108
|
+
if (typeof route !== 'string' || !/^[a-z][a-z0-9-]*$/.test(route) || routes.has(route)) {
|
|
109
|
+
throw new Error('Invalid or duplicate workflow route: ' + route);
|
|
110
|
+
}
|
|
111
|
+
routes.add(route);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return catalog;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Inline and reference-style Markdown links are packaged dependencies. */
|
|
118
|
+
function localLinks(content, file) {
|
|
119
|
+
const destinations = [
|
|
120
|
+
...Array.from(
|
|
121
|
+
content.matchAll(/!?\[[^\]\n]*\]\(<?([^\s)>]+)>?(?:\s+"[^"]*")?\)/g),
|
|
122
|
+
(m) => m[1]
|
|
123
|
+
),
|
|
124
|
+
...Array.from(content.matchAll(/^\s{0,3}\[[^\]\n]+\]:\s*<?([^\s>]+)>?/gm), (m) => m[1]),
|
|
125
|
+
];
|
|
126
|
+
return destinations
|
|
127
|
+
.filter((link) => !/^(?:https?:|mailto:|#)/i.test(link))
|
|
128
|
+
.map((link) => {
|
|
129
|
+
const clean = decodeURIComponent(link.split('#')[0]);
|
|
130
|
+
if (!clean || /[\\:\0?{}]/.test(clean) || path.posix.isAbsolute(clean)) {
|
|
131
|
+
throw new Error('Unsupported resource link in ' + file + ': ' + link);
|
|
132
|
+
}
|
|
133
|
+
return relativePath(path.posix.normalize(path.posix.join(path.posix.dirname(file), clean)));
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function validatePack(packRoot, expected) {
|
|
138
|
+
const catalog = loadCatalog(packRoot);
|
|
139
|
+
if (expected) {
|
|
140
|
+
const workflows = Object.values(expected.categories)
|
|
141
|
+
.flatMap((category) => category.workflows)
|
|
142
|
+
.sort();
|
|
143
|
+
if (
|
|
144
|
+
JSON.stringify(workflows) !== JSON.stringify(catalog.workflows.map((w) => w.id).sort()) ||
|
|
145
|
+
JSON.stringify([...expected.sub_agents].sort()) !==
|
|
146
|
+
JSON.stringify(catalog.agents.map((a) => a.id).sort())
|
|
147
|
+
) {
|
|
148
|
+
throw new Error('Dev Studio catalog disagrees with registry routes');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const queue = [
|
|
152
|
+
'SKILL.md',
|
|
153
|
+
'README.md',
|
|
154
|
+
'dev-studio-orchestrator.md',
|
|
155
|
+
'shared/execution.md',
|
|
156
|
+
...catalog.compatibility,
|
|
157
|
+
...catalog.agents.map((a) => a.path),
|
|
158
|
+
...catalog.workflows.flatMap((w) => [w.path, ...w.resources]),
|
|
159
|
+
];
|
|
160
|
+
const visited = new Set();
|
|
161
|
+
let bytes = 0;
|
|
162
|
+
while (queue.length) {
|
|
163
|
+
const file = queue.shift();
|
|
164
|
+
if (visited.has(file)) continue;
|
|
165
|
+
if (visited.size >= 256) throw new Error('Too many Dev Studio resources');
|
|
166
|
+
const resource = readText(packRoot, file);
|
|
167
|
+
bytes += Buffer.byteLength(resource.content);
|
|
168
|
+
if (bytes > MAX_CONTEXT) throw new Error('Dev Studio resource graph is too large');
|
|
169
|
+
visited.add(file);
|
|
170
|
+
if (file.endsWith('.md')) {
|
|
171
|
+
if (
|
|
172
|
+
/\{(?:workflow|agent)\.[^}]+\}|\{(?:project-root|planning_artifacts|communication_language)\}/.test(
|
|
173
|
+
resource.content
|
|
174
|
+
)
|
|
175
|
+
) {
|
|
176
|
+
throw new Error('Unresolved runtime declaration: ' + file);
|
|
177
|
+
}
|
|
178
|
+
queue.push(...localLinks(resource.content, file));
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { catalog, resources: [...visited].sort() };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function resolveConfig(projectDir) {
|
|
185
|
+
const file = '_bmad/config.yaml';
|
|
186
|
+
const location = target(projectDir, file);
|
|
187
|
+
let supplied = {};
|
|
188
|
+
let source = null;
|
|
189
|
+
if (fs.existsSync(location)) {
|
|
190
|
+
source = readText(projectDir, file, 64 * 1024);
|
|
191
|
+
try {
|
|
192
|
+
supplied = yaml.load(source.content);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
throw new Error(file + ': ' + error.message, { cause: error });
|
|
195
|
+
}
|
|
196
|
+
if (!supplied || typeof supplied !== 'object' || Array.isArray(supplied)) {
|
|
197
|
+
throw new Error(file + ' must be a YAML mapping');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const config = {
|
|
201
|
+
user_name: 'user',
|
|
202
|
+
communication_language: 'English',
|
|
203
|
+
document_output_language: 'English',
|
|
204
|
+
output_folder: '_bmad-output',
|
|
205
|
+
project_name: path.basename(projectDir),
|
|
206
|
+
};
|
|
207
|
+
const defaultsUsed = [];
|
|
208
|
+
for (const key of Object.keys(config)) {
|
|
209
|
+
if (Object.hasOwn(supplied, key)) {
|
|
210
|
+
if (typeof supplied[key] !== 'string' || !supplied[key].trim()) {
|
|
211
|
+
throw new Error(file + ': ' + key + ' must be a nonempty string');
|
|
212
|
+
}
|
|
213
|
+
config[key] = supplied[key];
|
|
214
|
+
} else defaultsUsed.push(key);
|
|
215
|
+
}
|
|
216
|
+
relativePath(config.output_folder);
|
|
217
|
+
return { values: config, defaultsUsed, source };
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function installedPack(projectDir) {
|
|
221
|
+
// Validate ancestors through an entry path before using the pack as a root.
|
|
222
|
+
target(projectDir, PACK + '/SKILL.md');
|
|
223
|
+
const packRoot = path.join(projectDir, PACK);
|
|
224
|
+
if (!fs.existsSync(packRoot))
|
|
225
|
+
throw new Error('Dev Studio is not installed. Run bmad-plus install --packs core,dev-studio');
|
|
226
|
+
return packRoot;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function prepare({
|
|
230
|
+
projectDir = process.cwd(),
|
|
231
|
+
workflow: route,
|
|
232
|
+
request = '',
|
|
233
|
+
inputs = [],
|
|
234
|
+
packRoot,
|
|
235
|
+
} = {}) {
|
|
236
|
+
projectDir = path.resolve(projectDir);
|
|
237
|
+
if (!fs.statSync(projectDir).isDirectory()) throw new Error('Project must be a directory');
|
|
238
|
+
packRoot = packRoot || installedPack(projectDir);
|
|
239
|
+
const { catalog } = validatePack(packRoot);
|
|
240
|
+
const workflow = catalog.workflows.find((w) => w.id === route || w.aliases.includes(route));
|
|
241
|
+
if (!workflow) throw new Error('Unknown Dev Studio workflow: ' + route);
|
|
242
|
+
if (
|
|
243
|
+
typeof request !== 'string' ||
|
|
244
|
+
Buffer.byteLength(request) > MAX_FILE ||
|
|
245
|
+
!Array.isArray(inputs) ||
|
|
246
|
+
inputs.length > 32
|
|
247
|
+
) {
|
|
248
|
+
throw new Error('Invalid or oversized workflow inputs');
|
|
249
|
+
}
|
|
250
|
+
const config = resolveConfig(projectDir);
|
|
251
|
+
const loadedInputs = [...new Set(inputs)].map((file) => readText(projectDir, file));
|
|
252
|
+
const agent = catalog.agents.find((a) => a.id === workflow.agent);
|
|
253
|
+
const routes = new Set([
|
|
254
|
+
...catalog.agents.map((a) => a.path),
|
|
255
|
+
...catalog.workflows.map((w) => w.path),
|
|
256
|
+
'SKILL.md',
|
|
257
|
+
'README.md',
|
|
258
|
+
'dev-studio-orchestrator.md',
|
|
259
|
+
'shared/catalog.json',
|
|
260
|
+
]);
|
|
261
|
+
const queue = ['shared/execution.md', agent.path, workflow.path, ...workflow.resources];
|
|
262
|
+
const instructions = [];
|
|
263
|
+
const loaded = new Set();
|
|
264
|
+
while (queue.length) {
|
|
265
|
+
const file = queue.shift();
|
|
266
|
+
if (loaded.has(file)) continue;
|
|
267
|
+
loaded.add(file);
|
|
268
|
+
const resource = readText(packRoot, file);
|
|
269
|
+
instructions.push(resource);
|
|
270
|
+
if (file.endsWith('.md'))
|
|
271
|
+
queue.push(...localLinks(resource.content, file).filter((link) => !routes.has(link)));
|
|
272
|
+
}
|
|
273
|
+
const outputPath = `${config.values.output_folder}/dev-studio/${workflow.id}.md`;
|
|
274
|
+
const outputTarget = target(projectDir, outputPath);
|
|
275
|
+
const previousReport = fs.existsSync(outputTarget) ? readText(projectDir, outputPath) : null;
|
|
276
|
+
const size = [
|
|
277
|
+
...loadedInputs,
|
|
278
|
+
...instructions,
|
|
279
|
+
...(previousReport ? [previousReport] : []),
|
|
280
|
+
].reduce(
|
|
281
|
+
(total, item) => total + Buffer.byteLength(item.content),
|
|
282
|
+
Buffer.byteLength(request) + Buffer.byteLength(config.source?.content || '')
|
|
283
|
+
);
|
|
284
|
+
if (size > MAX_CONTEXT) throw new Error('Prepared context exceeds 2 MiB');
|
|
285
|
+
const missingInputs =
|
|
286
|
+
workflow.input === 'artifact' && !loadedInputs.length
|
|
287
|
+
? ['Supply a project artifact with --input RELATIVE_FILE.']
|
|
288
|
+
: ['request', 'project'].includes(workflow.input) && !request.trim() && !loadedInputs.length
|
|
289
|
+
? ['Supply the task scope with --request TEXT or --input RELATIVE_FILE.']
|
|
290
|
+
: [];
|
|
291
|
+
return {
|
|
292
|
+
schemaVersion: 1,
|
|
293
|
+
status: missingInputs.length ? 'needs-input' : 'ready',
|
|
294
|
+
execution: 'host-managed',
|
|
295
|
+
executed: false,
|
|
296
|
+
workflow: workflow.id,
|
|
297
|
+
agent: agent.persona,
|
|
298
|
+
catalogSha256: readText(packRoot, 'shared/catalog.json').sha256,
|
|
299
|
+
projectDir,
|
|
300
|
+
config,
|
|
301
|
+
request,
|
|
302
|
+
inputs: loadedInputs,
|
|
303
|
+
instructions,
|
|
304
|
+
outputPath,
|
|
305
|
+
previousReport,
|
|
306
|
+
missingInputs,
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
module.exports = { loadCatalog, localLinks, validatePack, resolveConfig, installedPack, prepare };
|