@applica-software-guru/sdd-core 0.2.0 → 1.0.1

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 (44) hide show
  1. package/dist/agent/agent-defaults.d.ts +3 -0
  2. package/dist/agent/agent-defaults.d.ts.map +1 -0
  3. package/dist/agent/agent-defaults.js +13 -0
  4. package/dist/agent/agent-defaults.js.map +1 -0
  5. package/dist/agent/agent-runner.d.ts +9 -0
  6. package/dist/agent/agent-runner.d.ts.map +1 -0
  7. package/dist/agent/agent-runner.js +43 -0
  8. package/dist/agent/agent-runner.js.map +1 -0
  9. package/dist/index.d.ts +5 -2
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/parser/bug-parser.d.ts +8 -0
  14. package/dist/parser/bug-parser.d.ts.map +1 -0
  15. package/dist/parser/bug-parser.js +45 -0
  16. package/dist/parser/bug-parser.js.map +1 -0
  17. package/dist/prompt/apply-prompt-generator.d.ts +3 -0
  18. package/dist/prompt/apply-prompt-generator.d.ts.map +1 -0
  19. package/dist/prompt/apply-prompt-generator.js +67 -0
  20. package/dist/prompt/apply-prompt-generator.js.map +1 -0
  21. package/dist/scaffold/init.js +1 -1
  22. package/dist/scaffold/init.js.map +1 -1
  23. package/dist/scaffold/templates.d.ts +1 -1
  24. package/dist/scaffold/templates.d.ts.map +1 -1
  25. package/dist/scaffold/templates.js +55 -9
  26. package/dist/scaffold/templates.js.map +1 -1
  27. package/dist/sdd.d.ts +5 -1
  28. package/dist/sdd.d.ts.map +1 -1
  29. package/dist/sdd.js +36 -0
  30. package/dist/sdd.js.map +1 -1
  31. package/dist/types.d.ts +14 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/src/agent/agent-defaults.ts +12 -0
  35. package/src/agent/agent-runner.ts +54 -0
  36. package/src/index.ts +7 -1
  37. package/src/parser/bug-parser.ts +41 -0
  38. package/src/prompt/apply-prompt-generator.ts +82 -0
  39. package/src/scaffold/init.ts +1 -1
  40. package/src/scaffold/templates.ts +55 -9
  41. package/src/sdd.ts +42 -1
  42. package/src/types.ts +17 -0
  43. package/tests/apply.test.ts +119 -0
  44. package/tests/bug.test.ts +173 -0
@@ -0,0 +1,173 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import { mkdtemp, rm, writeFile, readFile, mkdir } from 'node:fs/promises';
3
+ import { existsSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
6
+ import { SDD } from '../src/sdd.js';
7
+ import { parseBugFile, discoverBugFiles } from '../src/parser/bug-parser.js';
8
+
9
+ const BUG_OPEN = `---
10
+ title: "Login fails with empty password"
11
+ status: open
12
+ author: "user"
13
+ created-at: "2025-01-01T00:00:00.000Z"
14
+ ---
15
+
16
+ ## Description
17
+
18
+ When the user submits the login form with an empty password, the app crashes.
19
+
20
+ ## Steps to reproduce
21
+
22
+ 1. Go to /login
23
+ 2. Enter email but leave password empty
24
+ 3. Click "Login"
25
+ `;
26
+
27
+ const BUG_RESOLVED = `---
28
+ title: "Navigation bar misaligned"
29
+ status: resolved
30
+ author: "user"
31
+ created-at: "2025-01-02T00:00:00.000Z"
32
+ ---
33
+
34
+ ## Description
35
+
36
+ The navigation bar is misaligned on mobile devices.
37
+ `;
38
+
39
+ describe('Bug parser', () => {
40
+ it('parses bug frontmatter correctly', () => {
41
+ const result = parseBugFile('bugs/BUG-001.md', BUG_OPEN);
42
+ expect(result.frontmatter.title).toBe('Login fails with empty password');
43
+ expect(result.frontmatter.status).toBe('open');
44
+ expect(result.frontmatter.author).toBe('user');
45
+ expect(result.frontmatter['created-at']).toBe('2025-01-01T00:00:00.000Z');
46
+ expect(result.body).toContain('## Description');
47
+ expect(result.body).toContain('empty password');
48
+ });
49
+
50
+ it('provides defaults for missing fields', () => {
51
+ const content = `---
52
+ title: "Minimal bug"
53
+ ---
54
+
55
+ Some body.
56
+ `;
57
+ const result = parseBugFile('test.md', content);
58
+ expect(result.frontmatter.status).toBe('open');
59
+ expect(result.frontmatter.author).toBe('');
60
+ expect(result.frontmatter['created-at']).toBe('');
61
+ });
62
+ });
63
+
64
+ describe('Bug file discovery', () => {
65
+ let tempDir: string;
66
+
67
+ beforeEach(async () => {
68
+ tempDir = await mkdtemp(join(tmpdir(), 'sdd-bug-discovery-'));
69
+ await mkdir(join(tempDir, 'bugs'), { recursive: true });
70
+ });
71
+
72
+ afterEach(async () => {
73
+ await rm(tempDir, { recursive: true });
74
+ });
75
+
76
+ it('discovers .md files in bugs/', async () => {
77
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_OPEN, 'utf-8');
78
+ await writeFile(join(tempDir, 'bugs/BUG-002.md'), BUG_RESOLVED, 'utf-8');
79
+
80
+ const files = await discoverBugFiles(tempDir);
81
+ expect(files).toHaveLength(2);
82
+ });
83
+
84
+ it('returns empty array when no bug files exist', async () => {
85
+ const files = await discoverBugFiles(tempDir);
86
+ expect(files).toHaveLength(0);
87
+ });
88
+ });
89
+
90
+ describe('SDD Bug methods', () => {
91
+ let tempDir: string;
92
+ let sdd: SDD;
93
+
94
+ beforeEach(async () => {
95
+ tempDir = await mkdtemp(join(tmpdir(), 'sdd-bug-'));
96
+ sdd = new SDD({ root: tempDir });
97
+ await sdd.init({ description: 'test' });
98
+ });
99
+
100
+ afterEach(async () => {
101
+ await rm(tempDir, { recursive: true });
102
+ });
103
+
104
+ it('bugs() returns all bugs', async () => {
105
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_OPEN, 'utf-8');
106
+ await writeFile(join(tempDir, 'bugs/BUG-002.md'), BUG_RESOLVED, 'utf-8');
107
+
108
+ const bugs = await sdd.bugs();
109
+ expect(bugs).toHaveLength(2);
110
+ expect(bugs[0].frontmatter.title).toBe('Login fails with empty password');
111
+ expect(bugs[1].frontmatter.title).toBe('Navigation bar misaligned');
112
+ });
113
+
114
+ it('openBugs() returns only open bugs', async () => {
115
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_OPEN, 'utf-8');
116
+ await writeFile(join(tempDir, 'bugs/BUG-002.md'), BUG_RESOLVED, 'utf-8');
117
+
118
+ const open = await sdd.openBugs();
119
+ expect(open).toHaveLength(1);
120
+ expect(open[0].frontmatter.status).toBe('open');
121
+ expect(open[0].frontmatter.title).toBe('Login fails with empty password');
122
+ });
123
+
124
+ it('markBugResolved() changes open to resolved', async () => {
125
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_OPEN, 'utf-8');
126
+
127
+ const marked = await sdd.markBugResolved(['bugs/BUG-001.md']);
128
+ expect(marked).toEqual(['bugs/BUG-001.md']);
129
+
130
+ const content = await readFile(join(tempDir, 'bugs/BUG-001.md'), 'utf-8');
131
+ expect(content).toContain('status: resolved');
132
+ });
133
+
134
+ it('markBugResolved() without args marks all open bugs', async () => {
135
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_OPEN, 'utf-8');
136
+ const open2 = BUG_OPEN.replace('Login fails with empty password', 'Second bug');
137
+ await writeFile(join(tempDir, 'bugs/BUG-002.md'), open2, 'utf-8');
138
+
139
+ const marked = await sdd.markBugResolved();
140
+ expect(marked).toHaveLength(2);
141
+ });
142
+
143
+ it('markBugResolved() skips already resolved bugs', async () => {
144
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_RESOLVED, 'utf-8');
145
+
146
+ const marked = await sdd.markBugResolved();
147
+ expect(marked).toHaveLength(0);
148
+ });
149
+
150
+ it('integration: create bug → open → mark resolved → no longer open', async () => {
151
+ await writeFile(join(tempDir, 'bugs/BUG-001.md'), BUG_OPEN, 'utf-8');
152
+
153
+ // Should be open
154
+ let open = await sdd.openBugs();
155
+ expect(open).toHaveLength(1);
156
+
157
+ // Mark as resolved
158
+ await sdd.markBugResolved();
159
+
160
+ // Should no longer be open
161
+ open = await sdd.openBugs();
162
+ expect(open).toHaveLength(0);
163
+
164
+ // But still in the full list
165
+ const all = await sdd.bugs();
166
+ expect(all).toHaveLength(1);
167
+ expect(all[0].frontmatter.status).toBe('resolved');
168
+ });
169
+
170
+ it('init creates bugs/ directory', async () => {
171
+ expect(existsSync(join(tempDir, 'bugs'))).toBe(true);
172
+ });
173
+ });