@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.
- package/dist/agent/agent-defaults.d.ts +3 -0
- package/dist/agent/agent-defaults.d.ts.map +1 -0
- package/dist/agent/agent-defaults.js +13 -0
- package/dist/agent/agent-defaults.js.map +1 -0
- package/dist/agent/agent-runner.d.ts +9 -0
- package/dist/agent/agent-runner.d.ts.map +1 -0
- package/dist/agent/agent-runner.js +43 -0
- package/dist/agent/agent-runner.js.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/parser/bug-parser.d.ts +8 -0
- package/dist/parser/bug-parser.d.ts.map +1 -0
- package/dist/parser/bug-parser.js +45 -0
- package/dist/parser/bug-parser.js.map +1 -0
- package/dist/prompt/apply-prompt-generator.d.ts +3 -0
- package/dist/prompt/apply-prompt-generator.d.ts.map +1 -0
- package/dist/prompt/apply-prompt-generator.js +67 -0
- package/dist/prompt/apply-prompt-generator.js.map +1 -0
- package/dist/scaffold/init.js +1 -1
- package/dist/scaffold/init.js.map +1 -1
- package/dist/scaffold/templates.d.ts +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +55 -9
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/sdd.d.ts +5 -1
- package/dist/sdd.d.ts.map +1 -1
- package/dist/sdd.js +36 -0
- package/dist/sdd.js.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agent/agent-defaults.ts +12 -0
- package/src/agent/agent-runner.ts +54 -0
- package/src/index.ts +7 -1
- package/src/parser/bug-parser.ts +41 -0
- package/src/prompt/apply-prompt-generator.ts +82 -0
- package/src/scaffold/init.ts +1 -1
- package/src/scaffold/templates.ts +55 -9
- package/src/sdd.ts +42 -1
- package/src/types.ts +17 -0
- package/tests/apply.test.ts +119 -0
- 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
|
+
});
|