bmad-studio 0.2.0 → 1.1.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/README.md +163 -17
- package/package.json +12 -3
- package/packages/client/dist/assets/index-81ZKe-R8.css +1 -0
- package/packages/client/dist/assets/index-DyjtzhqN.js +641 -0
- package/packages/client/dist/index.html +2 -2
- package/packages/server/dist/app.d.ts +2 -1
- package/packages/server/dist/app.d.ts.map +1 -1
- package/packages/server/dist/app.js +68 -3
- package/packages/server/dist/app.js.map +1 -1
- package/packages/server/dist/core/file-store.d.ts +8 -1
- package/packages/server/dist/core/file-store.d.ts.map +1 -1
- package/packages/server/dist/core/file-store.js +26 -3
- package/packages/server/dist/core/file-store.js.map +1 -1
- package/packages/server/dist/core/ide-skill-generator.d.ts +58 -0
- package/packages/server/dist/core/ide-skill-generator.d.ts.map +1 -0
- package/packages/server/dist/core/ide-skill-generator.js +270 -0
- package/packages/server/dist/core/ide-skill-generator.js.map +1 -0
- package/packages/server/dist/core/ide-skill-generator.test.d.ts +2 -0
- package/packages/server/dist/core/ide-skill-generator.test.d.ts.map +1 -0
- package/packages/server/dist/core/ide-skill-generator.test.js +257 -0
- package/packages/server/dist/core/ide-skill-generator.test.js.map +1 -0
- package/packages/server/dist/core/module-installer.d.ts +165 -0
- package/packages/server/dist/core/module-installer.d.ts.map +1 -0
- package/packages/server/dist/core/module-installer.js +445 -0
- package/packages/server/dist/core/module-installer.js.map +1 -0
- package/packages/server/dist/core/module-installer.test.d.ts +2 -0
- package/packages/server/dist/core/module-installer.test.d.ts.map +1 -0
- package/packages/server/dist/core/module-installer.test.js +509 -0
- package/packages/server/dist/core/module-installer.test.js.map +1 -0
- package/packages/server/dist/core/module-registry.d.ts +5 -0
- package/packages/server/dist/core/module-registry.d.ts.map +1 -0
- package/packages/server/dist/core/module-registry.js +109 -0
- package/packages/server/dist/core/module-registry.js.map +1 -0
- package/packages/server/dist/core/module-registry.test.d.ts +2 -0
- package/packages/server/dist/core/module-registry.test.d.ts.map +1 -0
- package/packages/server/dist/core/module-registry.test.js +280 -0
- package/packages/server/dist/core/module-registry.test.js.map +1 -0
- package/packages/server/dist/core/write-service.d.ts +20 -0
- package/packages/server/dist/core/write-service.d.ts.map +1 -1
- package/packages/server/dist/core/write-service.js +113 -1
- package/packages/server/dist/core/write-service.js.map +1 -1
- package/packages/server/dist/core/write-service.test.js +93 -6
- package/packages/server/dist/core/write-service.test.js.map +1 -1
- package/packages/server/dist/index.js +85 -1
- package/packages/server/dist/index.js.map +1 -1
- package/packages/server/dist/parsers/module-yaml-parser.d.ts +16 -0
- package/packages/server/dist/parsers/module-yaml-parser.d.ts.map +1 -0
- package/packages/server/dist/parsers/module-yaml-parser.js +62 -0
- package/packages/server/dist/parsers/module-yaml-parser.js.map +1 -0
- package/packages/server/dist/parsers/module-yaml-parser.test.d.ts +2 -0
- package/packages/server/dist/parsers/module-yaml-parser.test.d.ts.map +1 -0
- package/packages/server/dist/parsers/module-yaml-parser.test.js +156 -0
- package/packages/server/dist/parsers/module-yaml-parser.test.js.map +1 -0
- package/packages/server/dist/parsers/skill-parser.d.ts.map +1 -1
- package/packages/server/dist/parsers/skill-parser.js +41 -4
- package/packages/server/dist/parsers/skill-parser.js.map +1 -1
- package/packages/server/dist/parsers/skill-parser.test.js +4 -3
- package/packages/server/dist/parsers/skill-parser.test.js.map +1 -1
- package/packages/server/dist/plugins/agents-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/agents-plugin.js +60 -1
- package/packages/server/dist/plugins/agents-plugin.js.map +1 -1
- package/packages/server/dist/plugins/commands-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/commands-plugin.js +37 -10
- package/packages/server/dist/plugins/commands-plugin.js.map +1 -1
- package/packages/server/dist/plugins/datasources-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/datasources-plugin.js +101 -0
- package/packages/server/dist/plugins/datasources-plugin.js.map +1 -1
- package/packages/server/dist/plugins/modules-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/modules-plugin.js +905 -100
- package/packages/server/dist/plugins/modules-plugin.js.map +1 -1
- package/packages/server/dist/plugins/modules-plugin.test.js +1894 -3
- package/packages/server/dist/plugins/modules-plugin.test.js.map +1 -1
- package/packages/server/dist/plugins/outputs-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/outputs-plugin.js +111 -0
- package/packages/server/dist/plugins/outputs-plugin.js.map +1 -1
- package/packages/server/dist/plugins/overview-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/overview-plugin.js +35 -2
- package/packages/server/dist/plugins/overview-plugin.js.map +1 -1
- package/packages/server/dist/plugins/search-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/search-plugin.js +19 -2
- package/packages/server/dist/plugins/search-plugin.js.map +1 -1
- package/packages/server/dist/plugins/settings-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/settings-plugin.js +38 -1
- package/packages/server/dist/plugins/settings-plugin.js.map +1 -1
- package/packages/server/dist/plugins/settings-plugin.test.js +72 -0
- package/packages/server/dist/plugins/settings-plugin.test.js.map +1 -1
- package/packages/server/dist/plugins/teams-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/teams-plugin.js +6 -6
- package/packages/server/dist/plugins/teams-plugin.js.map +1 -1
- package/packages/server/dist/plugins/teams-plugin.test.js +43 -0
- package/packages/server/dist/plugins/teams-plugin.test.js.map +1 -1
- package/packages/server/dist/plugins/workflows-plugin.d.ts.map +1 -1
- package/packages/server/dist/plugins/workflows-plugin.js +14 -6
- package/packages/server/dist/plugins/workflows-plugin.js.map +1 -1
- package/packages/shared/src/config.ts +26 -0
- package/packages/shared/src/events.ts +7 -0
- package/packages/shared/src/index.ts +13 -0
- package/packages/shared/src/modules.ts +42 -0
- package/packages/shared/src/registry.ts +26 -0
- package/packages/shared/src/types.test.ts +37 -1
- package/packages/shared/src/workflows.ts +27 -0
- package/packages/client/dist/assets/index-5nXyrx_3.css +0 -1
- package/packages/client/dist/assets/index-DxN3uabX.js +0 -521
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { generateIdeSkillsForModule, removeIdeSkillsForModule, } from './ide-skill-generator.js';
|
|
6
|
+
// Minimal FileStore stub — the generator only uses studioDir + the watcher hooks.
|
|
7
|
+
function makeStubFileStore(studioDir) {
|
|
8
|
+
return {
|
|
9
|
+
studioDir,
|
|
10
|
+
markPendingWrite: () => { },
|
|
11
|
+
clearPendingWrite: () => { },
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function makeManifest(ides) {
|
|
16
|
+
return {
|
|
17
|
+
installation: {
|
|
18
|
+
version: '6.2.0',
|
|
19
|
+
installDate: '2026-01-01T00:00:00.000Z',
|
|
20
|
+
lastUpdated: '2026-01-01T00:00:00.000Z',
|
|
21
|
+
},
|
|
22
|
+
modules: [],
|
|
23
|
+
ides,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
describe('generateIdeSkillsForModule', () => {
|
|
27
|
+
let tmpDir;
|
|
28
|
+
let studioDir;
|
|
29
|
+
let fileStore;
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ide-skill-gen-')));
|
|
32
|
+
studioDir = path.join(tmpDir, '.bmad-studio');
|
|
33
|
+
fs.mkdirSync(studioDir, { recursive: true });
|
|
34
|
+
fileStore = makeStubFileStore(studioDir);
|
|
35
|
+
// Build a fixture module with one agent + one workflow + one task
|
|
36
|
+
const moduleDir = path.join(tmpDir, '_bmad', 'test-mod');
|
|
37
|
+
fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
|
|
38
|
+
fs.mkdirSync(path.join(moduleDir, 'workflows', 'create-foo'), { recursive: true });
|
|
39
|
+
fs.mkdirSync(path.join(moduleDir, 'tasks', 'preflight'), { recursive: true });
|
|
40
|
+
fs.writeFileSync(path.join(moduleDir, 'agents', 'architect.md'), [
|
|
41
|
+
'<agent id="architect" name="architect" title="Test Architect" capabilities="design">',
|
|
42
|
+
' <persona>',
|
|
43
|
+
' <identity>I design systems</identity>',
|
|
44
|
+
' </persona>',
|
|
45
|
+
'</agent>',
|
|
46
|
+
].join('\n'));
|
|
47
|
+
fs.writeFileSync(path.join(moduleDir, 'workflows', 'create-foo', 'workflow.md'), '---\nname: create-foo\n---\n# Create Foo\n\n**Goal:** Creates a foo\n');
|
|
48
|
+
fs.writeFileSync(path.join(moduleDir, 'tasks', 'preflight', 'task.md'), '---\nname: preflight\n---\n# Preflight\n');
|
|
49
|
+
});
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
52
|
+
});
|
|
53
|
+
// ─── AC-15.6.1 ───
|
|
54
|
+
it('AC-15.6.1: generates SKILL.md files for agents and workflows under .claude/skills/', () => {
|
|
55
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
56
|
+
expect(result.ok).toBe(true);
|
|
57
|
+
if (!result.ok)
|
|
58
|
+
return;
|
|
59
|
+
const agentSkill = path.join(tmpDir, '.claude/skills/bmad-agent-test-mod-architect/SKILL.md');
|
|
60
|
+
const workflowSkill = path.join(tmpDir, '.claude/skills/bmad-test-mod-create-foo/SKILL.md');
|
|
61
|
+
const taskSkill = path.join(tmpDir, '.claude/skills/bmad-test-mod-preflight/SKILL.md');
|
|
62
|
+
expect(fs.existsSync(agentSkill)).toBe(true);
|
|
63
|
+
expect(fs.existsSync(workflowSkill)).toBe(true);
|
|
64
|
+
expect(fs.existsSync(taskSkill)).toBe(true);
|
|
65
|
+
// Per-IDE counts
|
|
66
|
+
expect(result.skillsByIde['claude-code']).toHaveLength(3);
|
|
67
|
+
});
|
|
68
|
+
// ─── AC-15.6.2 ───
|
|
69
|
+
it('AC-15.6.2: generates files under both .claude/skills/ and .antigravity/skills/', () => {
|
|
70
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code', 'antigravity']), studioDir, fileStore);
|
|
71
|
+
expect(result.ok).toBe(true);
|
|
72
|
+
if (!result.ok)
|
|
73
|
+
return;
|
|
74
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-test-mod-architect/SKILL.md'))).toBe(true);
|
|
75
|
+
expect(fs.existsSync(path.join(tmpDir, '.antigravity/skills/bmad-agent-test-mod-architect/SKILL.md'))).toBe(true);
|
|
76
|
+
expect(result.skillsByIde['claude-code']).toHaveLength(3);
|
|
77
|
+
expect(result.skillsByIde.antigravity).toHaveLength(3);
|
|
78
|
+
});
|
|
79
|
+
// ─── AC-15.6.3 ───
|
|
80
|
+
it('AC-15.6.3: agent description is extracted via parseAgent (XML title)', () => {
|
|
81
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
82
|
+
expect(result.ok).toBe(true);
|
|
83
|
+
const skillContent = fs.readFileSync(path.join(tmpDir, '.claude/skills/bmad-agent-test-mod-architect/SKILL.md'), 'utf-8');
|
|
84
|
+
expect(skillContent).toContain('description: "Test Architect"');
|
|
85
|
+
});
|
|
86
|
+
// ─── AC-15.6.4 ───
|
|
87
|
+
it('AC-15.6.4: agent without a description falls back to "BMAD <name> agent from <module>"', () => {
|
|
88
|
+
// Replace the architect file with one that has no <agent> block
|
|
89
|
+
fs.writeFileSync(path.join(tmpDir, '_bmad', 'test-mod', 'agents', 'plain.md'), '# Just a plain agent\n');
|
|
90
|
+
// Remove the original architect to keep things clean
|
|
91
|
+
fs.unlinkSync(path.join(tmpDir, '_bmad', 'test-mod', 'agents', 'architect.md'));
|
|
92
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
93
|
+
expect(result.ok).toBe(true);
|
|
94
|
+
const skillContent = fs.readFileSync(path.join(tmpDir, '.claude/skills/bmad-agent-test-mod-plain/SKILL.md'), 'utf-8');
|
|
95
|
+
expect(skillContent).toContain('description: "BMAD plain agent from test-mod"');
|
|
96
|
+
});
|
|
97
|
+
// ─── AC-15.6.5 ───
|
|
98
|
+
it('AC-15.6.5: SKILL.md body contains an absolute path to the source file', () => {
|
|
99
|
+
generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
100
|
+
const skillContent = fs.readFileSync(path.join(tmpDir, '.claude/skills/bmad-agent-test-mod-architect/SKILL.md'), 'utf-8');
|
|
101
|
+
// The body should contain an absolute path (starts with / on Unix)
|
|
102
|
+
expect(skillContent).toContain(path.join(tmpDir, '_bmad', 'test-mod', 'agents', 'architect.md'));
|
|
103
|
+
// And the path is absolute
|
|
104
|
+
expect(skillContent).toMatch(/^\/.*architect\.md$/m);
|
|
105
|
+
});
|
|
106
|
+
// ─── AC-15.6.6 ───
|
|
107
|
+
it('AC-15.6.6: calling generator twice produces byte-identical files', () => {
|
|
108
|
+
const result1 = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
109
|
+
expect(result1.ok).toBe(true);
|
|
110
|
+
const skillPath = path.join(tmpDir, '.claude/skills/bmad-agent-test-mod-architect/SKILL.md');
|
|
111
|
+
const firstContent = fs.readFileSync(skillPath, 'utf-8');
|
|
112
|
+
// Snapshot history count BEFORE the second call
|
|
113
|
+
const historyDir = path.join(studioDir, 'history');
|
|
114
|
+
const historyBefore = fs.existsSync(historyDir) ? fs.readdirSync(historyDir).length : 0;
|
|
115
|
+
const result2 = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
116
|
+
expect(result2.ok).toBe(true);
|
|
117
|
+
const secondContent = fs.readFileSync(skillPath, 'utf-8');
|
|
118
|
+
expect(secondContent).toBe(firstContent);
|
|
119
|
+
// The idempotent early-exit means no new history entries on the second call
|
|
120
|
+
const historyAfter = fs.existsSync(historyDir) ? fs.readdirSync(historyDir).length : 0;
|
|
121
|
+
expect(historyAfter).toBe(historyBefore);
|
|
122
|
+
});
|
|
123
|
+
// ─── AC-15.6.7 ───
|
|
124
|
+
it('AC-15.6.7: unknown IDEs are silently filtered (cursor)', () => {
|
|
125
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code', 'cursor', 'antigravity']), studioDir, fileStore);
|
|
126
|
+
expect(result.ok).toBe(true);
|
|
127
|
+
if (!result.ok)
|
|
128
|
+
return;
|
|
129
|
+
// Both supported IDEs got launchers
|
|
130
|
+
expect(result.skillsByIde['claude-code']).toHaveLength(3);
|
|
131
|
+
expect(result.skillsByIde.antigravity).toHaveLength(3);
|
|
132
|
+
// The unknown IDE was silently dropped
|
|
133
|
+
expect(result.skillsByIde.cursor).toBeUndefined();
|
|
134
|
+
expect(fs.existsSync(path.join(tmpDir, '.cursor', 'skills'))).toBe(false);
|
|
135
|
+
});
|
|
136
|
+
// ─── AC-15.6.9 ───
|
|
137
|
+
it('AC-15.6.9: empty manifest.ides[] is a no-op', () => {
|
|
138
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest([]), studioDir, fileStore);
|
|
139
|
+
expect(result).toEqual({ ok: true, skillsByIde: {} });
|
|
140
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude'))).toBe(false);
|
|
141
|
+
expect(fs.existsSync(path.join(tmpDir, '.antigravity'))).toBe(false);
|
|
142
|
+
});
|
|
143
|
+
// ─── AC-15.6.9 (b) ───
|
|
144
|
+
it('AC-15.6.9 (b): missing manifest.ides field is a no-op', () => {
|
|
145
|
+
const manifestNoIdes = {
|
|
146
|
+
installation: {
|
|
147
|
+
version: '6.2.0',
|
|
148
|
+
installDate: '2026-01-01',
|
|
149
|
+
lastUpdated: '2026-01-01',
|
|
150
|
+
},
|
|
151
|
+
modules: [],
|
|
152
|
+
// ides field intentionally omitted
|
|
153
|
+
};
|
|
154
|
+
const result = generateIdeSkillsForModule(tmpDir, 'test-mod', manifestNoIdes, studioDir, fileStore);
|
|
155
|
+
expect(result).toEqual({ ok: true, skillsByIde: {} });
|
|
156
|
+
});
|
|
157
|
+
// ─── workflow description extraction (AC-15.6.3 supporting case) ───
|
|
158
|
+
it('extracts workflow description via parseWorkflow', () => {
|
|
159
|
+
generateIdeSkillsForModule(tmpDir, 'test-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
160
|
+
const skillContent = fs.readFileSync(path.join(tmpDir, '.claude/skills/bmad-test-mod-create-foo/SKILL.md'), 'utf-8');
|
|
161
|
+
// The fixture workflow has `description: Creates a foo` in the frontmatter
|
|
162
|
+
expect(skillContent).toContain('Creates a foo');
|
|
163
|
+
});
|
|
164
|
+
// ─── module with no entities is a no-op for that IDE ───
|
|
165
|
+
it('module with no agents/workflows/tasks generates an empty list per IDE', () => {
|
|
166
|
+
// Create an empty module
|
|
167
|
+
const emptyModuleDir = path.join(tmpDir, '_bmad', 'empty-mod');
|
|
168
|
+
fs.mkdirSync(emptyModuleDir, { recursive: true });
|
|
169
|
+
const result = generateIdeSkillsForModule(tmpDir, 'empty-mod', makeManifest(['claude-code']), studioDir, fileStore);
|
|
170
|
+
expect(result.ok).toBe(true);
|
|
171
|
+
if (!result.ok)
|
|
172
|
+
return;
|
|
173
|
+
expect(result.skillsByIde['claude-code']).toEqual([]);
|
|
174
|
+
// The output dir is created but is empty (no SKILL.md files written)
|
|
175
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude/skills'))).toBe(true);
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
179
|
+
// removeIdeSkillsForModule — Story 15.7
|
|
180
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
181
|
+
describe('removeIdeSkillsForModule', () => {
|
|
182
|
+
let tmpDir;
|
|
183
|
+
let studioDir;
|
|
184
|
+
let fileStore;
|
|
185
|
+
beforeEach(() => {
|
|
186
|
+
tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'ide-skill-remove-')));
|
|
187
|
+
studioDir = path.join(tmpDir, '.bmad-studio');
|
|
188
|
+
fs.mkdirSync(studioDir, { recursive: true });
|
|
189
|
+
fileStore = makeStubFileStore(studioDir);
|
|
190
|
+
});
|
|
191
|
+
afterEach(() => {
|
|
192
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
193
|
+
});
|
|
194
|
+
// ─── AC-15.7.3a ───
|
|
195
|
+
it('AC-15.7.3a: removes only prefix-matched directories, leaves others alone', () => {
|
|
196
|
+
// First, generate skills for two modules using generateIdeSkillsForModule so the
|
|
197
|
+
// fixture matches real usage.
|
|
198
|
+
const setupModule = (code) => {
|
|
199
|
+
const moduleDir = path.join(tmpDir, '_bmad', code);
|
|
200
|
+
fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
|
|
201
|
+
fs.writeFileSync(path.join(moduleDir, 'agents', 'a.md'), `<agent id="a" name="a" title="${code} agent" capabilities="work"></agent>\n`);
|
|
202
|
+
};
|
|
203
|
+
setupModule('foo');
|
|
204
|
+
setupModule('bar');
|
|
205
|
+
generateIdeSkillsForModule(tmpDir, 'foo', makeManifest(['claude-code']), studioDir, fileStore);
|
|
206
|
+
generateIdeSkillsForModule(tmpDir, 'bar', makeManifest(['claude-code']), studioDir, fileStore);
|
|
207
|
+
// Both dirs exist before removal
|
|
208
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-foo-a'))).toBe(true);
|
|
209
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-bar-a'))).toBe(true);
|
|
210
|
+
// Remove foo — only foo's dirs should go
|
|
211
|
+
const result = removeIdeSkillsForModule(tmpDir, 'foo', makeManifest(['claude-code']), studioDir);
|
|
212
|
+
expect(result.ok).toBe(true);
|
|
213
|
+
if (!result.ok)
|
|
214
|
+
return;
|
|
215
|
+
expect(result.removedByIde['claude-code']).toEqual(['bmad-agent-foo-a']);
|
|
216
|
+
// foo removed, bar untouched
|
|
217
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-foo-a'))).toBe(false);
|
|
218
|
+
expect(fs.existsSync(path.join(tmpDir, '.claude/skills/bmad-agent-bar-a'))).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
// ─── AC-15.7.3b ───
|
|
221
|
+
it('AC-15.7.3b: missing IDE dir returns empty list, no error', () => {
|
|
222
|
+
const result = removeIdeSkillsForModule(tmpDir, 'never-installed', makeManifest(['claude-code', 'antigravity']), studioDir);
|
|
223
|
+
expect(result.ok).toBe(true);
|
|
224
|
+
if (!result.ok)
|
|
225
|
+
return;
|
|
226
|
+
expect(result.removedByIde).toEqual({
|
|
227
|
+
'claude-code': [],
|
|
228
|
+
antigravity: [],
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
// ─── AC-15.7.3c ───
|
|
232
|
+
it('AC-15.7.3c: removed files are recoverable from .bmad-studio/history/', () => {
|
|
233
|
+
// Install a module
|
|
234
|
+
const moduleDir = path.join(tmpDir, '_bmad', 'recover-test');
|
|
235
|
+
fs.mkdirSync(path.join(moduleDir, 'agents'), { recursive: true });
|
|
236
|
+
fs.writeFileSync(path.join(moduleDir, 'agents', 'helper.md'), '<agent id="helper" name="helper" title="Helper" capabilities="help"></agent>\n');
|
|
237
|
+
generateIdeSkillsForModule(tmpDir, 'recover-test', makeManifest(['claude-code']), studioDir, fileStore);
|
|
238
|
+
// Remove
|
|
239
|
+
const result = removeIdeSkillsForModule(tmpDir, 'recover-test', makeManifest(['claude-code']), studioDir);
|
|
240
|
+
expect(result.ok).toBe(true);
|
|
241
|
+
// SKILL.md from the removed dir should have a snapshot in .bmad-studio/history/
|
|
242
|
+
const historyDir = path.join(studioDir, 'history');
|
|
243
|
+
expect(fs.existsSync(historyDir)).toBe(true);
|
|
244
|
+
const history = fs.readdirSync(historyDir);
|
|
245
|
+
expect(history.some((n) => n.endsWith('SKILL.md'))).toBe(true);
|
|
246
|
+
});
|
|
247
|
+
// Ignores unknown IDEs (parallel to AC-15.6.7)
|
|
248
|
+
it('ignores unknown IDE entries when scanning for removal', () => {
|
|
249
|
+
const result = removeIdeSkillsForModule(tmpDir, 'whatever', makeManifest(['claude-code', 'cursor']), studioDir);
|
|
250
|
+
expect(result.ok).toBe(true);
|
|
251
|
+
if (!result.ok)
|
|
252
|
+
return;
|
|
253
|
+
expect(result.removedByIde['claude-code']).toEqual([]);
|
|
254
|
+
expect(result.removedByIde.cursor).toBeUndefined();
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
//# sourceMappingURL=ide-skill-generator.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ide-skill-generator.test.js","sourceRoot":"","sources":["../../src/core/ide-skill-generator.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { ModuleManifestFile } from '@bmad-studio/shared';
|
|
2
|
+
import type { EntityIndex } from '../parsers/index-builder.js';
|
|
3
|
+
import type { FileStore } from './file-store.js';
|
|
4
|
+
export { TEXT_FILE_EXTENSIONS, isLikelyText } from './write-service.js';
|
|
5
|
+
export type CopyDirResult = {
|
|
6
|
+
ok: true;
|
|
7
|
+
textCount: number;
|
|
8
|
+
binaryCount: number;
|
|
9
|
+
} | {
|
|
10
|
+
ok: false;
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Walk a source directory and copy every file into a destination.
|
|
15
|
+
*
|
|
16
|
+
* Text files (per TEXT_FILE_EXTENSIONS) are routed through WriteService so snapshots are written.
|
|
17
|
+
* Binary files are copied via fs.copyFileSync — no snapshot, no corruption (see TD-16).
|
|
18
|
+
*
|
|
19
|
+
* Replaces the legacy fs.copyFileSync-based copyDirRecursive helper that lived in modules-plugin.ts.
|
|
20
|
+
*/
|
|
21
|
+
export declare function copyDirThroughWriteService(src: string, dest: string, studioDir: string, fileStore: FileStore): CopyDirResult;
|
|
22
|
+
/**
|
|
23
|
+
* Read manifest.yaml; null-safe for missing files.
|
|
24
|
+
*/
|
|
25
|
+
export declare function readManifestSafe(manifestPath: string): ModuleManifestFile | null;
|
|
26
|
+
/**
|
|
27
|
+
* Write manifest.yaml through WriteService so changes are snapshot.
|
|
28
|
+
*/
|
|
29
|
+
export declare function writeManifestThroughWriteService(manifestPath: string, manifest: ModuleManifestFile, studioDir: string, fileStore: FileStore): {
|
|
30
|
+
ok: true;
|
|
31
|
+
} | {
|
|
32
|
+
ok: false;
|
|
33
|
+
error: string;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Verify a local path looks like a module — has a module.yaml OR contains at least
|
|
37
|
+
* one of the standard entity directories.
|
|
38
|
+
*/
|
|
39
|
+
export declare function isPlausibleModuleDir(dir: string): boolean;
|
|
40
|
+
export type GithubSource = {
|
|
41
|
+
owner: string;
|
|
42
|
+
repo: string;
|
|
43
|
+
subpath: string | null;
|
|
44
|
+
branch: string | null;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Lenient parser for GitHub source strings. Accepts any of:
|
|
48
|
+
* owner/repo
|
|
49
|
+
* owner/repo/subpath
|
|
50
|
+
* owner/repo/nested/subpath
|
|
51
|
+
* owner/repo@branch
|
|
52
|
+
* owner/repo/subpath@branch
|
|
53
|
+
* https://github.com/owner/repo
|
|
54
|
+
* https://github.com/owner/repo/tree/branch
|
|
55
|
+
* https://github.com/owner/repo/tree/branch/subpath
|
|
56
|
+
*
|
|
57
|
+
* Throws plain Error on invalid input — the install handler is responsible for
|
|
58
|
+
* catching and converting to ValidationError.
|
|
59
|
+
*/
|
|
60
|
+
export declare function parseGithubSource(input: string): GithubSource;
|
|
61
|
+
export type DownloadedTarball = {
|
|
62
|
+
extractedRoot: string;
|
|
63
|
+
tmpDir: string;
|
|
64
|
+
resolvedBranch: string;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Download a GitHub repo tarball into a temp dir, extract, and return the
|
|
68
|
+
* extracted root path along with the branch that was actually used.
|
|
69
|
+
*
|
|
70
|
+
* Tries `main` first then `master` if no branch is given (per Q5). Honours
|
|
71
|
+
* GITHUB_TOKEN / BMAD_GITHUB_TOKEN env vars for private repos. Throws
|
|
72
|
+
* immediately on 401/403 (token errors don't fix themselves on `master`).
|
|
73
|
+
*
|
|
74
|
+
* The caller is responsible for cleaning up `tmpDir` after consuming
|
|
75
|
+
* `extractedRoot` (typically in a `try { ... } finally { fs.rmSync(tmpDir) }`).
|
|
76
|
+
*/
|
|
77
|
+
export declare function downloadGithubTarball(source: GithubSource): Promise<DownloadedTarball>;
|
|
78
|
+
export type ExtractedZip = {
|
|
79
|
+
extractedRoot: string;
|
|
80
|
+
tmpDir: string;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Extract a multipart-uploaded zip into a temp dir and return the module root.
|
|
84
|
+
*
|
|
85
|
+
* `adm-zip` is loaded via dynamic import (TD-5) so users who never upload a zip
|
|
86
|
+
* pay zero startup cost. The first call pays the ~10ms parse, subsequent calls
|
|
87
|
+
* hit the Node.js module cache for free.
|
|
88
|
+
*
|
|
89
|
+
* Includes inline zip-slip mitigation (finding #22): every entry in the zip is
|
|
90
|
+
* checked to ensure its resolved path stays inside extractDir BEFORE extraction.
|
|
91
|
+
* The entire upload is rejected on any escape attempt, and no file is written
|
|
92
|
+
* to disk.
|
|
93
|
+
*
|
|
94
|
+
* If the zip's top-level contains exactly one directory, that directory is
|
|
95
|
+
* returned as `extractedRoot` (the "wrapper dir" case — most zip exports include
|
|
96
|
+
* one). Otherwise the extract dir itself is returned.
|
|
97
|
+
*
|
|
98
|
+
* The caller is responsible for cleaning up `tmpDir` after consuming
|
|
99
|
+
* `extractedRoot` (typically in a `try { ... } finally { fs.rmSync(tmpDir) }`).
|
|
100
|
+
*/
|
|
101
|
+
export declare function extractZipUpload(zipBuffer: Buffer): Promise<ExtractedZip>;
|
|
102
|
+
export declare const SUBSTITUTABLE_EXTENSIONS: ReadonlySet<string>;
|
|
103
|
+
export type SubstitutionContext = {
|
|
104
|
+
moduleCode: string;
|
|
105
|
+
projectRoot: string;
|
|
106
|
+
outputFolder: string;
|
|
107
|
+
variables: Record<string, string>;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Validate that all variable values are safe to substitute into text/yaml/csv files.
|
|
111
|
+
* Returns the FIRST violation as an error message naming the offending variable.
|
|
112
|
+
*/
|
|
113
|
+
export declare function validateVariables(variables: Record<string, string>): {
|
|
114
|
+
ok: true;
|
|
115
|
+
} | {
|
|
116
|
+
ok: false;
|
|
117
|
+
error: string;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Walk a destination directory and substitute placeholders in files matching
|
|
121
|
+
* SUBSTITUTABLE_EXTENSIONS. Files without any matching placeholder are left
|
|
122
|
+
* untouched (no spurious snapshots). Substitution writes go through WriteService.
|
|
123
|
+
*
|
|
124
|
+
* Returns the count of files actually patched (a no-placeholder file does not count).
|
|
125
|
+
*/
|
|
126
|
+
export declare function runVariableSubstitution(destDir: string, ctx: SubstitutionContext, studioDir: string, fileStore: FileStore): {
|
|
127
|
+
ok: true;
|
|
128
|
+
filesPatched: number;
|
|
129
|
+
} | {
|
|
130
|
+
ok: false;
|
|
131
|
+
error: string;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Read the project's `output_folder` setting from `_bmad/_config/config.yaml`.
|
|
135
|
+
* Returns `<projectRoot>/_bmad-output` as the default if the file is missing,
|
|
136
|
+
* malformed, or doesn't declare the field.
|
|
137
|
+
*/
|
|
138
|
+
export declare function readOutputFolder(projectRoot: string): string;
|
|
139
|
+
export type CrossReference = {
|
|
140
|
+
ownerModule: string;
|
|
141
|
+
reason: string;
|
|
142
|
+
};
|
|
143
|
+
/**
|
|
144
|
+
* Scan the FileStore entity index for cross-module references to a target
|
|
145
|
+
* module's agents. Used by the remove preview endpoint to warn users about
|
|
146
|
+
* broken references after removal.
|
|
147
|
+
*
|
|
148
|
+
* **TD-19 scope (v1):** only scans two signals:
|
|
149
|
+
* 1. Teams whose `agentIds[]` contains any target-module agent ID
|
|
150
|
+
* 2. Workflow steps whose `step.agent` matches a target-module agent ID
|
|
151
|
+
*
|
|
152
|
+
* **Explicitly NOT scanned in v1:**
|
|
153
|
+
* - `agent.menu[].route` — holds a relative file path from the XML `exec=`
|
|
154
|
+
* attribute, not a workflow ID. `Set<workflowId>.has(filepath)` always
|
|
155
|
+
* returns false, so the check would be dead code.
|
|
156
|
+
* - `agent.skills[]` — hardcoded to `[]` in `agent-parser.ts:93`. Dead field.
|
|
157
|
+
*
|
|
158
|
+
* Both will be revisited in v2 with proper graph extraction.
|
|
159
|
+
*
|
|
160
|
+
* Returns an array of `{ ownerModule, reason }` entries, deduplicated by
|
|
161
|
+
* owner+reason. The target module itself is never included. Unattributed
|
|
162
|
+
* entities (edge case) are reported under `"(unattributed)"`.
|
|
163
|
+
*/
|
|
164
|
+
export declare function findCrossReferences(index: EntityIndex, targetModule: string): CrossReference[];
|
|
165
|
+
//# sourceMappingURL=module-installer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module-installer.d.ts","sourceRoot":"","sources":["../../src/core/module-installer.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAA;AAE9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAMhD,OAAO,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAEvE,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhC;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,GACnB,aAAa,CA0Cf;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI,CAIhF;AAED;;GAEG;AACH,wBAAgB,gCAAgC,CAC9C,YAAY,EAAE,MAAM,EACpB,QAAQ,EAAE,kBAAkB,EAC5B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,GACnB;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAM7C;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAMzD;AAMD,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAqC7D;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6E5F;AAMD,MAAM,MAAM,YAAY,GAAG;IACzB,aAAa,EAAE,MAAM,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAmD/E;AAUD,eAAO,MAAM,wBAAwB,EAAE,WAAW,CAAC,MAAM,CAOvD,CAAA;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC,CAAA;AAQD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAU7C;AAeD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,mBAAmB,EACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,SAAS,GACnB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CA8BnE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAY5D;AAMD,MAAM,MAAM,cAAc,GAAG;IAC3B,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,GAAG,cAAc,EAAE,CAqC9F"}
|