bmad-studio 0.2.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +6 -0
  2. package/package.json +12 -3
  3. package/packages/client/dist/assets/index-CWL4J-eZ.css +1 -0
  4. package/packages/client/dist/assets/index-DBqsFqD5.js +535 -0
  5. package/packages/client/dist/index.html +2 -2
  6. package/packages/server/dist/app.d.ts +1 -0
  7. package/packages/server/dist/app.d.ts.map +1 -1
  8. package/packages/server/dist/app.js +9 -0
  9. package/packages/server/dist/app.js.map +1 -1
  10. package/packages/server/dist/core/ide-skill-generator.d.ts +58 -0
  11. package/packages/server/dist/core/ide-skill-generator.d.ts.map +1 -0
  12. package/packages/server/dist/core/ide-skill-generator.js +270 -0
  13. package/packages/server/dist/core/ide-skill-generator.js.map +1 -0
  14. package/packages/server/dist/core/ide-skill-generator.test.d.ts +2 -0
  15. package/packages/server/dist/core/ide-skill-generator.test.d.ts.map +1 -0
  16. package/packages/server/dist/core/ide-skill-generator.test.js +257 -0
  17. package/packages/server/dist/core/ide-skill-generator.test.js.map +1 -0
  18. package/packages/server/dist/core/module-installer.d.ts +165 -0
  19. package/packages/server/dist/core/module-installer.d.ts.map +1 -0
  20. package/packages/server/dist/core/module-installer.js +445 -0
  21. package/packages/server/dist/core/module-installer.js.map +1 -0
  22. package/packages/server/dist/core/module-installer.test.d.ts +2 -0
  23. package/packages/server/dist/core/module-installer.test.d.ts.map +1 -0
  24. package/packages/server/dist/core/module-installer.test.js +509 -0
  25. package/packages/server/dist/core/module-installer.test.js.map +1 -0
  26. package/packages/server/dist/core/module-registry.d.ts +5 -0
  27. package/packages/server/dist/core/module-registry.d.ts.map +1 -0
  28. package/packages/server/dist/core/module-registry.js +109 -0
  29. package/packages/server/dist/core/module-registry.js.map +1 -0
  30. package/packages/server/dist/core/module-registry.test.d.ts +2 -0
  31. package/packages/server/dist/core/module-registry.test.d.ts.map +1 -0
  32. package/packages/server/dist/core/module-registry.test.js +280 -0
  33. package/packages/server/dist/core/module-registry.test.js.map +1 -0
  34. package/packages/server/dist/core/write-service.d.ts +20 -0
  35. package/packages/server/dist/core/write-service.d.ts.map +1 -1
  36. package/packages/server/dist/core/write-service.js +113 -1
  37. package/packages/server/dist/core/write-service.js.map +1 -1
  38. package/packages/server/dist/core/write-service.test.js +93 -6
  39. package/packages/server/dist/core/write-service.test.js.map +1 -1
  40. package/packages/server/dist/index.js +46 -1
  41. package/packages/server/dist/index.js.map +1 -1
  42. package/packages/server/dist/parsers/module-yaml-parser.d.ts +16 -0
  43. package/packages/server/dist/parsers/module-yaml-parser.d.ts.map +1 -0
  44. package/packages/server/dist/parsers/module-yaml-parser.js +62 -0
  45. package/packages/server/dist/parsers/module-yaml-parser.js.map +1 -0
  46. package/packages/server/dist/parsers/module-yaml-parser.test.d.ts +2 -0
  47. package/packages/server/dist/parsers/module-yaml-parser.test.d.ts.map +1 -0
  48. package/packages/server/dist/parsers/module-yaml-parser.test.js +156 -0
  49. package/packages/server/dist/parsers/module-yaml-parser.test.js.map +1 -0
  50. package/packages/server/dist/parsers/skill-parser.d.ts.map +1 -1
  51. package/packages/server/dist/parsers/skill-parser.js +41 -4
  52. package/packages/server/dist/parsers/skill-parser.js.map +1 -1
  53. package/packages/server/dist/parsers/skill-parser.test.js +4 -3
  54. package/packages/server/dist/parsers/skill-parser.test.js.map +1 -1
  55. package/packages/server/dist/plugins/agents-plugin.d.ts.map +1 -1
  56. package/packages/server/dist/plugins/agents-plugin.js +9 -1
  57. package/packages/server/dist/plugins/agents-plugin.js.map +1 -1
  58. package/packages/server/dist/plugins/modules-plugin.d.ts.map +1 -1
  59. package/packages/server/dist/plugins/modules-plugin.js +882 -100
  60. package/packages/server/dist/plugins/modules-plugin.js.map +1 -1
  61. package/packages/server/dist/plugins/modules-plugin.test.js +1894 -3
  62. package/packages/server/dist/plugins/modules-plugin.test.js.map +1 -1
  63. package/packages/server/dist/plugins/overview-plugin.js +1 -1
  64. package/packages/server/dist/plugins/overview-plugin.js.map +1 -1
  65. package/packages/server/dist/plugins/settings-plugin.d.ts.map +1 -1
  66. package/packages/server/dist/plugins/settings-plugin.js +25 -1
  67. package/packages/server/dist/plugins/settings-plugin.js.map +1 -1
  68. package/packages/server/dist/plugins/settings-plugin.test.js +72 -0
  69. package/packages/server/dist/plugins/settings-plugin.test.js.map +1 -1
  70. package/packages/server/dist/plugins/teams-plugin.d.ts.map +1 -1
  71. package/packages/server/dist/plugins/teams-plugin.js +6 -6
  72. package/packages/server/dist/plugins/teams-plugin.js.map +1 -1
  73. package/packages/server/dist/plugins/teams-plugin.test.js +43 -0
  74. package/packages/server/dist/plugins/teams-plugin.test.js.map +1 -1
  75. package/packages/server/dist/plugins/workflows-plugin.d.ts.map +1 -1
  76. package/packages/server/dist/plugins/workflows-plugin.js +14 -6
  77. package/packages/server/dist/plugins/workflows-plugin.js.map +1 -1
  78. package/packages/shared/src/config.ts +26 -0
  79. package/packages/shared/src/index.ts +12 -0
  80. package/packages/shared/src/modules.ts +42 -0
  81. package/packages/shared/src/registry.ts +26 -0
  82. package/packages/shared/src/types.test.ts +37 -1
  83. package/packages/client/dist/assets/index-5nXyrx_3.css +0 -1
  84. package/packages/client/dist/assets/index-DxN3uabX.js +0 -521
@@ -2,12 +2,12 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
2
  import fs from 'node:fs';
3
3
  import os from 'node:os';
4
4
  import path from 'node:path';
5
- import { writeFile, getHistory } from './write-service.js';
5
+ import { deleteDirectory, deleteFile, getHistory, writeFile } from './write-service.js';
6
6
  describe('write-service', () => {
7
7
  let tmpDir;
8
8
  let studioDir;
9
9
  beforeEach(() => {
10
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'write-test-'));
10
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'write-test-')));
11
11
  studioDir = path.join(tmpDir, '.bmad-studio');
12
12
  });
13
13
  afterEach(() => {
@@ -38,18 +38,18 @@ describe('write-service', () => {
38
38
  expect(result.snapshotPath).toBeNull();
39
39
  }
40
40
  });
41
- it('prunes history when exceeding 50 snapshots', () => {
41
+ it('prunes history when exceeding HISTORY_CAP (500 after TD-17)', () => {
42
42
  const historyDir = path.join(studioDir, 'history');
43
43
  fs.mkdirSync(historyDir, { recursive: true });
44
- // Create 52 existing snapshots
45
- for (let i = 0; i < 52; i++) {
44
+ // Create 502 existing snapshots
45
+ for (let i = 0; i < 502; i++) {
46
46
  fs.writeFileSync(path.join(historyDir, `${1000 + i}-old.md`), `snapshot ${i}`);
47
47
  }
48
48
  const filePath = path.join(tmpDir, 'test.md');
49
49
  fs.writeFileSync(filePath, 'existing');
50
50
  writeFile(filePath, 'updated', studioDir);
51
51
  const remaining = fs.readdirSync(historyDir);
52
- expect(remaining.length).toBeLessThanOrEqual(50);
52
+ expect(remaining.length).toBeLessThanOrEqual(500);
53
53
  });
54
54
  it('getHistory returns sorted list of snapshots', () => {
55
55
  const historyDir = path.join(studioDir, 'history');
@@ -65,5 +65,92 @@ describe('write-service', () => {
65
65
  expect(result.ok).toBe(true); // ensureDir creates parents
66
66
  expect(fs.readFileSync(filePath, 'utf-8')).toBe('content');
67
67
  });
68
+ // ───────────────────────────────────────────────────────────────────────────
69
+ // Story 15.7 — deleteFile / deleteDirectory
70
+ // ───────────────────────────────────────────────────────────────────────────
71
+ describe('deleteFile', () => {
72
+ it('AC-15.7.1: snapshots a text file to history/ then unlinks', () => {
73
+ const filePath = path.join(tmpDir, 'doc.md');
74
+ fs.writeFileSync(filePath, '# Original content\n');
75
+ const result = deleteFile(filePath, studioDir);
76
+ expect(result.ok).toBe(true);
77
+ if (!result.ok)
78
+ return;
79
+ // File is gone
80
+ expect(fs.existsSync(filePath)).toBe(false);
81
+ // Snapshot exists in history/
82
+ expect(result.snapshotPath).not.toBeNull();
83
+ expect(result.snapshotPath).toBeTruthy();
84
+ const historyEntries = fs.readdirSync(path.join(studioDir, 'history'));
85
+ const snap = historyEntries.find((n) => n.endsWith('doc.md'));
86
+ expect(snap).toBeDefined();
87
+ expect(fs.readFileSync(path.join(studioDir, 'history', snap), 'utf-8')).toBe('# Original content\n');
88
+ });
89
+ it('TD-16: binary file unlinks without snapshot', () => {
90
+ const filePath = path.join(tmpDir, 'logo.png');
91
+ // PNG header bytes (intentionally invalid utf-8 so any accidental decode breaks)
92
+ fs.writeFileSync(filePath, Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a]));
93
+ const result = deleteFile(filePath, studioDir);
94
+ expect(result.ok).toBe(true);
95
+ if (!result.ok)
96
+ return;
97
+ // File is gone
98
+ expect(fs.existsSync(filePath)).toBe(false);
99
+ // snapshotPath is null (no snapshot for binaries)
100
+ expect(result.snapshotPath).toBeNull();
101
+ // No .png files in history/ at all
102
+ const historyDir = path.join(studioDir, 'history');
103
+ if (fs.existsSync(historyDir)) {
104
+ const entries = fs.readdirSync(historyDir);
105
+ expect(entries.some((n) => n.endsWith('.png'))).toBe(false);
106
+ }
107
+ });
108
+ it('AC-15.7.3: returns { ok: false } on missing path (does not throw)', () => {
109
+ const result = deleteFile(path.join(tmpDir, 'ghost.md'), studioDir);
110
+ expect(result.ok).toBe(false);
111
+ if (result.ok)
112
+ return;
113
+ expect(result.error).toBe('File does not exist');
114
+ });
115
+ });
116
+ describe('deleteDirectory', () => {
117
+ it('AC-15.7.2: snapshots text files before unlinking the tree', () => {
118
+ const dir = path.join(tmpDir, 'mod');
119
+ fs.mkdirSync(path.join(dir, 'agents'), { recursive: true });
120
+ fs.writeFileSync(path.join(dir, 'agents', 'a.md'), '# Agent A\n');
121
+ fs.writeFileSync(path.join(dir, 'agents', 'b.md'), '# Agent B\n');
122
+ fs.writeFileSync(path.join(dir, 'config.yaml'), 'key: value\n');
123
+ const result = deleteDirectory(dir, studioDir);
124
+ expect(result.ok).toBe(true);
125
+ // The directory is gone
126
+ expect(fs.existsSync(dir)).toBe(false);
127
+ // All three text files snapshotted
128
+ const history = fs.readdirSync(path.join(studioDir, 'history'));
129
+ expect(history.some((n) => n.endsWith('a.md'))).toBe(true);
130
+ expect(history.some((n) => n.endsWith('b.md'))).toBe(true);
131
+ expect(history.some((n) => n.endsWith('config.yaml'))).toBe(true);
132
+ });
133
+ it('TD-16: mixed text + binary dir snapshots only text', () => {
134
+ const dir = path.join(tmpDir, 'mod');
135
+ fs.mkdirSync(path.join(dir, 'assets'), { recursive: true });
136
+ fs.writeFileSync(path.join(dir, 'readme.md'), '# Readme\n');
137
+ fs.writeFileSync(path.join(dir, 'assets', 'icon.png'), Buffer.from([0x89, 0x50, 0x4e, 0x47]));
138
+ const result = deleteDirectory(dir, studioDir);
139
+ expect(result.ok).toBe(true);
140
+ // Directory is gone
141
+ expect(fs.existsSync(dir)).toBe(false);
142
+ // readme.md snapshotted, icon.png NOT
143
+ const history = fs.readdirSync(path.join(studioDir, 'history'));
144
+ expect(history.some((n) => n.endsWith('readme.md'))).toBe(true);
145
+ expect(history.some((n) => n.endsWith('.png'))).toBe(false);
146
+ });
147
+ it('AC-15.7.3: returns { ok: false } on missing directory', () => {
148
+ const result = deleteDirectory(path.join(tmpDir, 'ghost-dir'), studioDir);
149
+ expect(result.ok).toBe(false);
150
+ if (result.ok)
151
+ return;
152
+ expect(result.error).toBe('Directory does not exist');
153
+ });
154
+ });
68
155
  });
69
156
  //# sourceMappingURL=write-service.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"write-service.test.js","sourceRoot":"","sources":["../../src/core/write-service.test.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"write-service.test.js","sourceRoot":"","sources":["../../src/core/write-service.test.ts"],"names":[],"mappings":"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"}
@@ -1,8 +1,50 @@
1
1
  import fs from 'node:fs';
2
+ import path from 'node:path';
2
3
  import { createApp } from './app.js';
3
4
  import { detectProject } from './core/project-detector.js';
4
5
  const DEFAULT_PORT = 4040;
5
6
  const MAX_PORT_RETRIES = 10;
7
+ /**
8
+ * Read the settings file from the studio directory.
9
+ * Returns null if the file doesn't exist or can't be parsed.
10
+ */
11
+ function readSettingsFile(studioDir) {
12
+ if (!studioDir)
13
+ return null;
14
+ const settingsPath = path.join(studioDir, 'settings.json');
15
+ if (!fs.existsSync(settingsPath))
16
+ return null;
17
+ try {
18
+ return JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
19
+ }
20
+ catch {
21
+ return null;
22
+ }
23
+ }
24
+ /**
25
+ * Build Fastify logger options. When file logging is enabled in settings,
26
+ * configures pino to write to both stdout and .bmad-studio/logs/studio.log.
27
+ */
28
+ function buildLoggerOptions(settings, studioDir, verbose) {
29
+ const fileLogging = settings?.logging?.enabled && studioDir;
30
+ const level = verbose ? 'debug' : (settings?.logging?.level ?? 'info');
31
+ if (!fileLogging) {
32
+ // Default: stdout only (Fastify default behaviour)
33
+ return verbose ? { level: 'debug' } : true;
34
+ }
35
+ const logsDir = path.join(studioDir, 'logs');
36
+ const logFile = path.join(logsDir, 'studio.log');
37
+ console.log(`File logging enabled — writing to ${logFile}`);
38
+ return {
39
+ level,
40
+ transport: {
41
+ targets: [
42
+ { target: 'pino/file', level, options: { destination: 1 } }, // stdout (fd 1)
43
+ { target: 'pino/file', level, options: { destination: logFile, mkdir: true } },
44
+ ],
45
+ },
46
+ };
47
+ }
6
48
  function parseArgs(args) {
7
49
  let port = DEFAULT_PORT;
8
50
  let dir;
@@ -56,8 +98,11 @@ async function main() {
56
98
  console.warn('No BMAD project found. Run from project root or use --dir /path');
57
99
  console.warn('Starting in setup mode...');
58
100
  }
101
+ const studioDir = project ? path.join(project.projectRoot, '.bmad-studio') : null;
102
+ const settings = readSettingsFile(studioDir);
103
+ const loggerOptions = buildLoggerOptions(settings, studioDir, verbose);
59
104
  const app = await createApp({
60
- logger: verbose ? { level: 'debug' } : true,
105
+ logger: loggerOptions,
61
106
  project,
62
107
  });
63
108
  // Listen with port auto-increment on EADDRINUSE
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ import type { ModuleYaml } from '@bmad-studio/shared';
2
+ import type { ParseResult } from './config-parser.js';
3
+ /**
4
+ * Parse a module's `module.yaml` metadata file.
5
+ *
6
+ * Takes a directory path (not a file path) because `module.yaml` is OPTIONAL.
7
+ * Legacy v6 modules like `dept-aem` ship without one — for these, the parser
8
+ * returns a graceful fallback derived from the directory basename. See TD-14
9
+ * in tech-spec-bmad-studio-module-manager-v1.md for the rationale.
10
+ *
11
+ * Returns:
12
+ * - `{ ok: true, data }` when the file is absent (fallback) OR present and valid.
13
+ * - `{ ok: false, error }` only when the file exists but is empty / not an object / malformed YAML.
14
+ */
15
+ export declare function parseModuleYaml(moduleDir: string): ParseResult<ModuleYaml>;
16
+ //# sourceMappingURL=module-yaml-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-yaml-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/module-yaml-parser.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAErD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC,UAAU,CAAC,CAgD1E"}
@@ -0,0 +1,62 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import yaml from 'js-yaml';
4
+ /**
5
+ * Parse a module's `module.yaml` metadata file.
6
+ *
7
+ * Takes a directory path (not a file path) because `module.yaml` is OPTIONAL.
8
+ * Legacy v6 modules like `dept-aem` ship without one — for these, the parser
9
+ * returns a graceful fallback derived from the directory basename. See TD-14
10
+ * in tech-spec-bmad-studio-module-manager-v1.md for the rationale.
11
+ *
12
+ * Returns:
13
+ * - `{ ok: true, data }` when the file is absent (fallback) OR present and valid.
14
+ * - `{ ok: false, error }` only when the file exists but is empty / not an object / malformed YAML.
15
+ */
16
+ export function parseModuleYaml(moduleDir) {
17
+ const yamlPath = path.join(moduleDir, 'module.yaml');
18
+ const fallbackCode = path.basename(moduleDir);
19
+ // Graceful fallback for the common case where module.yaml is absent.
20
+ if (!fs.existsSync(yamlPath)) {
21
+ return {
22
+ ok: true,
23
+ data: {
24
+ code: fallbackCode,
25
+ version: '1.0.0',
26
+ variables: {},
27
+ directories: [],
28
+ },
29
+ };
30
+ }
31
+ try {
32
+ const content = fs.readFileSync(yamlPath, 'utf-8');
33
+ const data = yaml.load(content);
34
+ if (!data || typeof data !== 'object') {
35
+ return {
36
+ ok: false,
37
+ error: 'module.yaml is empty or not an object',
38
+ filePath: yamlPath,
39
+ };
40
+ }
41
+ return {
42
+ ok: true,
43
+ data: {
44
+ code: (data.code ?? fallbackCode).toString(),
45
+ name: data.name,
46
+ version: data.version ?? '1.0.0',
47
+ description: data.description,
48
+ variables: data.variables ?? {},
49
+ directories: data.directories ?? [],
50
+ },
51
+ };
52
+ }
53
+ catch (err) {
54
+ const message = err instanceof Error ? err.message : String(err);
55
+ return {
56
+ ok: false,
57
+ error: `module.yaml parse error: ${message}`,
58
+ filePath: yamlPath,
59
+ };
60
+ }
61
+ }
62
+ //# sourceMappingURL=module-yaml-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-yaml-parser.js","sourceRoot":"","sources":["../../src/parsers/module-yaml-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,IAAI,MAAM,SAAS,CAAA;AAM1B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,eAAe,CAAC,SAAiB;IAC/C,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAA;IACpD,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;IAE7C,qEAAqE;IACrE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,EAAE,EAAE,IAAI;YACR,IAAI,EAAE;gBACJ,IAAI,EAAE,YAAY;gBAClB,OAAO,EAAE,OAAO;gBAChB,SAAS,EAAE,EAAE;gBACb,WAAW,EAAE,EAAE;aAChB;SACF,CAAA;IACH,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QAClD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAA+B,CAAA;QAE7D,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtC,OAAO;gBACL,EAAE,EAAE,KAAK;gBACT,KAAK,EAAE,uCAAuC;gBAC9C,QAAQ,EAAE,QAAQ;aACnB,CAAA;QACH,CAAC;QAED,OAAO;YACL,EAAE,EAAE,IAAI;YACR,IAAI,EAAE;gBACJ,IAAI,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,YAAY,CAAC,CAAC,QAAQ,EAAE;gBAC5C,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,OAAO;gBAChC,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,EAAE;gBAC/B,WAAW,EAAE,IAAI,CAAC,WAAW,IAAI,EAAE;aACpC;SACF,CAAA;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChE,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,4BAA4B,OAAO,EAAE;YAC5C,QAAQ,EAAE,QAAQ;SACnB,CAAA;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=module-yaml-parser.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-yaml-parser.test.d.ts","sourceRoot":"","sources":["../../src/parsers/module-yaml-parser.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,156 @@
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 { parseModuleYaml } from './module-yaml-parser.js';
6
+ describe('parseModuleYaml', () => {
7
+ let tmpDir;
8
+ beforeEach(() => {
9
+ // TD-20: realpathSync resolves the macOS /var → /private/var symlink so
10
+ // path comparisons against tmpDir are stable.
11
+ tmpDir = fs.realpathSync(fs.mkdtempSync(path.join(os.tmpdir(), 'module-yaml-parser-test-')));
12
+ });
13
+ afterEach(() => {
14
+ fs.rmSync(tmpDir, { recursive: true, force: true });
15
+ });
16
+ // AC-15.1.1
17
+ it('returns the fallback when module.yaml is absent', () => {
18
+ const moduleDir = path.join(tmpDir, 'dept-aem');
19
+ fs.mkdirSync(moduleDir);
20
+ const result = parseModuleYaml(moduleDir);
21
+ expect(result.ok).toBe(true);
22
+ if (!result.ok)
23
+ return;
24
+ expect(result.data).toEqual({
25
+ code: 'dept-aem',
26
+ version: '1.0.0',
27
+ variables: {},
28
+ directories: [],
29
+ });
30
+ });
31
+ // AC-15.1.2
32
+ it('parses a module.yaml with all fields populated, with no field loss', () => {
33
+ const moduleDir = path.join(tmpDir, 'full-module');
34
+ fs.mkdirSync(moduleDir);
35
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), [
36
+ 'code: full-module',
37
+ 'name: "Full Featured Module"',
38
+ 'version: "2.1.0"',
39
+ 'description: "A module that exercises every field"',
40
+ 'variables:',
41
+ ' project_name:',
42
+ ' prompt: "What is the project name?"',
43
+ ' default: "my-project"',
44
+ ' region:',
45
+ ' prompt: "Deployment region?"',
46
+ ' default: "us-east-1"',
47
+ 'directories:',
48
+ ' - "_bmad-output/full-module-artifacts"',
49
+ ' - "_bmad-output/full-module-cache"',
50
+ ].join('\n'));
51
+ const result = parseModuleYaml(moduleDir);
52
+ expect(result.ok).toBe(true);
53
+ if (!result.ok)
54
+ return;
55
+ expect(result.data).toEqual({
56
+ code: 'full-module',
57
+ name: 'Full Featured Module',
58
+ version: '2.1.0',
59
+ description: 'A module that exercises every field',
60
+ variables: {
61
+ project_name: { prompt: 'What is the project name?', default: 'my-project' },
62
+ region: { prompt: 'Deployment region?', default: 'us-east-1' },
63
+ },
64
+ directories: [
65
+ '_bmad-output/full-module-artifacts',
66
+ '_bmad-output/full-module-cache',
67
+ ],
68
+ });
69
+ });
70
+ // AC-15.1.3
71
+ it('applies defaults when module.yaml only contains code', () => {
72
+ const moduleDir = path.join(tmpDir, 'minimal');
73
+ fs.mkdirSync(moduleDir);
74
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), 'code: dept-aem\n');
75
+ const result = parseModuleYaml(moduleDir);
76
+ expect(result.ok).toBe(true);
77
+ if (!result.ok)
78
+ return;
79
+ expect(result.data.code).toBe('dept-aem');
80
+ expect(result.data.version).toBe('1.0.0');
81
+ expect(result.data.variables).toEqual({});
82
+ expect(result.data.directories).toEqual([]);
83
+ expect(result.data.name).toBeUndefined();
84
+ expect(result.data.description).toBeUndefined();
85
+ });
86
+ // AC-15.1.3 supporting case — variables block parses correctly
87
+ it('parses a variables block with one entry', () => {
88
+ const moduleDir = path.join(tmpDir, 'with-vars');
89
+ fs.mkdirSync(moduleDir);
90
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), [
91
+ 'code: with-vars',
92
+ 'variables:',
93
+ ' aem_project_name:',
94
+ ' prompt: "What is the AEM project name?"',
95
+ ' default: "my-aem-project"',
96
+ ].join('\n'));
97
+ const result = parseModuleYaml(moduleDir);
98
+ expect(result.ok).toBe(true);
99
+ if (!result.ok)
100
+ return;
101
+ expect(result.data.variables).toEqual({
102
+ aem_project_name: { prompt: 'What is the AEM project name?', default: 'my-aem-project' },
103
+ });
104
+ });
105
+ // AC-15.1.3 supporting case — directories array parses correctly
106
+ it('parses a directories array', () => {
107
+ const moduleDir = path.join(tmpDir, 'with-dirs');
108
+ fs.mkdirSync(moduleDir);
109
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), [
110
+ 'code: with-dirs',
111
+ 'directories:',
112
+ ' - "_bmad-output/aem-artifacts"',
113
+ ].join('\n'));
114
+ const result = parseModuleYaml(moduleDir);
115
+ expect(result.ok).toBe(true);
116
+ if (!result.ok)
117
+ return;
118
+ expect(result.data.directories).toEqual(['_bmad-output/aem-artifacts']);
119
+ });
120
+ // AC-15.1.4
121
+ it('returns ok:false when module.yaml is empty', () => {
122
+ const moduleDir = path.join(tmpDir, 'empty-yaml');
123
+ fs.mkdirSync(moduleDir);
124
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), '');
125
+ const result = parseModuleYaml(moduleDir);
126
+ expect(result.ok).toBe(false);
127
+ if (result.ok)
128
+ return;
129
+ expect(result.error).toBe('module.yaml is empty or not an object');
130
+ expect(result.filePath).toBe(path.join(moduleDir, 'module.yaml'));
131
+ });
132
+ // AC-15.1.4 supporting case — whitespace-only file
133
+ it('returns ok:false when module.yaml contains only whitespace', () => {
134
+ const moduleDir = path.join(tmpDir, 'whitespace-yaml');
135
+ fs.mkdirSync(moduleDir);
136
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), ' \n \n');
137
+ const result = parseModuleYaml(moduleDir);
138
+ expect(result.ok).toBe(false);
139
+ if (result.ok)
140
+ return;
141
+ expect(result.error).toBe('module.yaml is empty or not an object');
142
+ });
143
+ // AC-15.1.5
144
+ it('returns ok:false with a parse error message when YAML is malformed', () => {
145
+ const moduleDir = path.join(tmpDir, 'broken-yaml');
146
+ fs.mkdirSync(moduleDir);
147
+ fs.writeFileSync(path.join(moduleDir, 'module.yaml'), 'code: [unclosed\n');
148
+ const result = parseModuleYaml(moduleDir);
149
+ expect(result.ok).toBe(false);
150
+ if (result.ok)
151
+ return;
152
+ expect(result.error.startsWith('module.yaml parse error')).toBe(true);
153
+ expect(result.filePath).toBe(path.join(moduleDir, 'module.yaml'));
154
+ });
155
+ });
156
+ //# sourceMappingURL=module-yaml-parser.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"module-yaml-parser.test.js","sourceRoot":"","sources":["../../src/parsers/module-yaml-parser.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"skill-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/skill-parser.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAErD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CAmBhF"}
1
+ {"version":3,"file":"skill-parser.d.ts","sourceRoot":"","sources":["../../src/parsers/skill-parser.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAA;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAuBrD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,CAAC,KAAK,CAAC,CA0ChF"}
@@ -1,12 +1,49 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
1
3
  import matter from 'gray-matter';
4
+ import yaml from 'js-yaml';
5
+ function readSiblingManifest(skillFilePath) {
6
+ const dir = path.dirname(skillFilePath);
7
+ const manifestPath = path.join(dir, 'bmad-skill-manifest.yaml');
8
+ if (!fs.existsSync(manifestPath))
9
+ return null;
10
+ try {
11
+ const content = fs.readFileSync(manifestPath, 'utf-8');
12
+ return yaml.load(content);
13
+ }
14
+ catch {
15
+ return null;
16
+ }
17
+ }
2
18
  export function parseSkill(filePath, content) {
3
19
  try {
4
20
  const { data: frontmatter, content: body } = matter(content);
21
+ let id = frontmatter.name || '';
22
+ let name = frontmatter.name || '';
23
+ let description = frontmatter.description || '';
24
+ let bestFor = frontmatter.best_for;
25
+ // Fall back to sibling manifest when frontmatter is missing or has no name
26
+ if (!name) {
27
+ const manifest = readSiblingManifest(filePath);
28
+ if (manifest?.skill) {
29
+ id = manifest.skill.canonicalId || manifest.skill.name || '';
30
+ name = manifest.skill.name || '';
31
+ description = manifest.skill.description || '';
32
+ }
33
+ if (!bestFor && manifest?.topics) {
34
+ bestFor = manifest.topics;
35
+ }
36
+ // Last resort: derive name from directory
37
+ if (!name) {
38
+ name = path.basename(path.dirname(filePath));
39
+ id = id || name;
40
+ }
41
+ }
5
42
  const skill = {
6
- id: frontmatter.name || '',
7
- name: frontmatter.name || '',
8
- description: frontmatter.description || '',
9
- bestFor: frontmatter.best_for,
43
+ id,
44
+ name,
45
+ description,
46
+ bestFor,
10
47
  content: body.trim(),
11
48
  filePath,
12
49
  module: undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"skill-parser.js","sourceRoot":"","sources":["../../src/parsers/skill-parser.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAA;AAMhC,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,OAAe;IAC1D,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;QAE5D,MAAM,KAAK,GAAU;YACnB,EAAE,EAAG,WAAW,CAAC,IAAe,IAAI,EAAE;YACtC,IAAI,EAAG,WAAW,CAAC,IAAe,IAAI,EAAE;YACxC,WAAW,EAAG,WAAW,CAAC,WAAsB,IAAI,EAAE;YACtD,OAAO,EAAE,WAAW,CAAC,QAAgC;YACrD,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE;YACpB,QAAQ;YACR,MAAM,EAAE,SAAS;SAClB,CAAA;QAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAA;IACxE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"skill-parser.js","sourceRoot":"","sources":["../../src/parsers/skill-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAE5B,OAAO,MAAM,MAAM,aAAa,CAAA;AAChC,OAAO,IAAI,MAAM,SAAS,CAAA;AAe1B,SAAS,mBAAmB,CAAC,aAAqB;IAChD,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAA;IACvC,MAAM,YAAY,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,0BAA0B,CAAC,CAAA;IAC/D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,IAAI,CAAA;IAC7C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;QACtD,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAkB,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,QAAgB,EAAE,OAAe;IAC1D,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;QAE5D,IAAI,EAAE,GAAI,WAAW,CAAC,IAAe,IAAI,EAAE,CAAA;QAC3C,IAAI,IAAI,GAAI,WAAW,CAAC,IAAe,IAAI,EAAE,CAAA;QAC7C,IAAI,WAAW,GAAI,WAAW,CAAC,WAAsB,IAAI,EAAE,CAAA;QAC3D,IAAI,OAAO,GAAG,WAAW,CAAC,QAAgC,CAAA;QAE1D,2EAA2E;QAC3E,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,QAAQ,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAA;YAC9C,IAAI,QAAQ,EAAE,KAAK,EAAE,CAAC;gBACpB,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,IAAI,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAA;gBAC5D,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAA;gBAChC,WAAW,GAAG,QAAQ,CAAC,KAAK,CAAC,WAAW,IAAI,EAAE,CAAA;YAChD,CAAC;YACD,IAAI,CAAC,OAAO,IAAI,QAAQ,EAAE,MAAM,EAAE,CAAC;gBACjC,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAA;YAC3B,CAAC;YACD,0CAA0C;YAC1C,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAA;gBAC5C,EAAE,GAAG,EAAE,IAAI,IAAI,CAAA;YACjB,CAAC;QACH,CAAC;QAED,MAAM,KAAK,GAAU;YACnB,EAAE;YACF,IAAI;YACJ,WAAW;YACX,OAAO;YACP,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE;YACpB,QAAQ;YACR,MAAM,EAAE,SAAS;SAClB,CAAA;QAED,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAA;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QAChE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,OAAO,EAAE,EAAE,QAAQ,EAAE,CAAA;IACxE,CAAC;AACH,CAAC"}
@@ -36,11 +36,12 @@ Content here.
36
36
  expect(result.data.bestFor).toEqual(['pm', 'analyst']);
37
37
  }
38
38
  });
39
- it('handles missing frontmatter gracefully', () => {
40
- const result = parseSkill('plain.md', 'Just plain markdown without frontmatter.');
39
+ it('handles missing frontmatter by falling back to directory name', () => {
40
+ const result = parseSkill('/projects/test/_bmad/core/skills/my-skill/SKILL.md', 'Just plain markdown without frontmatter.');
41
41
  expect(result.ok).toBe(true);
42
42
  if (result.ok) {
43
- expect(result.data.id).toBe('');
43
+ expect(result.data.id).toBe('my-skill');
44
+ expect(result.data.name).toBe('my-skill');
44
45
  expect(result.data.content).toBe('Just plain markdown without frontmatter.');
45
46
  }
46
47
  });
@@ -1 +1 @@
1
- {"version":3,"file":"skill-parser.test.js","sourceRoot":"","sources":["../../src/parsers/skill-parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG;;;;;;CAMnB,CAAA;QACG,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAC9C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;YAC9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;YAChD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;YAClE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;YAC7E,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,OAAO,GAAG;;;;;;;;;CASnB,CAAA;QACG,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC7C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAA;QACxD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,EAAE,0CAA0C,CAAC,CAAA;QACjF,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;YAC/B,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;QAC9E,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
1
+ {"version":3,"file":"skill-parser.test.js","sourceRoot":"","sources":["../../src/parsers/skill-parser.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG;;;;;;CAMnB,CAAA;QACG,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;QAC9C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;YAC9C,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAA;YAChD,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAA;YAClE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAA;YAC7E,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAC/C,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,OAAO,GAAG;;;;;;;;;CASnB,CAAA;QACG,MAAM,MAAM,GAAG,UAAU,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;QAC7C,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAA;QACxD,CAAC;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+DAA+D,EAAE,GAAG,EAAE;QACvE,MAAM,MAAM,GAAG,UAAU,CAAC,oDAAoD,EAAE,0CAA0C,CAAC,CAAA;QAC3H,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QAC5B,IAAI,MAAM,CAAC,EAAE,EAAE,CAAC;YACd,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YACvC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;YACzC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAA;QAC9E,CAAC;IACH,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"agents-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/agents-plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqB9C,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,iBA4ItD"}
1
+ {"version":3,"file":"agents-plugin.d.ts","sourceRoot":"","sources":["../../src/plugins/agents-plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAqB9C,wBAAsB,YAAY,CAAC,GAAG,EAAE,eAAe,iBAmJtD"}
@@ -107,7 +107,15 @@ export async function agentsPlugin(app) {
107
107
  const skillsYaml = skills.map((s) => ` - ${s}`).join('\n');
108
108
  const skillsPattern = /^(skills:\s*\n)((?:\s+-\s+.+\n)*)/m;
109
109
  if (skillsPattern.test(source)) {
110
- source = source.replace(skillsPattern, `$1${skillsYaml}\n`);
110
+ source = source.replace(skillsPattern, `$1${skillsYaml ? skillsYaml + '\n' : ''}`);
111
+ }
112
+ else {
113
+ // No skills: block in frontmatter — insert before the closing ---
114
+ const closingFm = source.indexOf('\n---');
115
+ if (closingFm !== -1) {
116
+ const block = skillsYaml ? `\nskills:\n${skillsYaml}` : `\nskills:`;
117
+ source = source.slice(0, closingFm) + block + source.slice(closingFm);
118
+ }
111
119
  }
112
120
  app.fileStore.markPendingWrite(agent.filePath);
113
121
  const result = writeFile(agent.filePath, source, app.fileStore.studioDir);