@applica-software-guru/sdd-core 1.0.0 → 1.3.3
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 +10 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- 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 +48 -0
- package/dist/prompt/apply-prompt-generator.js.map +1 -0
- package/dist/prompt/prompt-generator.d.ts.map +1 -1
- package/dist/prompt/prompt-generator.js +7 -11
- package/dist/prompt/prompt-generator.js.map +1 -1
- package/dist/scaffold/init.d.ts +1 -1
- package/dist/scaffold/init.d.ts.map +1 -1
- package/dist/scaffold/init.js +10 -29
- package/dist/scaffold/init.js.map +1 -1
- package/dist/scaffold/skill-adapters.d.ts +39 -0
- package/dist/scaffold/skill-adapters.d.ts.map +1 -0
- package/dist/scaffold/skill-adapters.js +224 -0
- package/dist/scaffold/skill-adapters.js.map +1 -0
- package/dist/scaffold/templates.d.ts +5 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +203 -55
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/sdd.d.ts +7 -3
- package/dist/sdd.d.ts.map +1 -1
- package/dist/sdd.js +36 -18
- package/dist/sdd.js.map +1 -1
- package/dist/types.d.ts +2 -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 +17 -5
- package/src/prompt/apply-prompt-generator.ts +58 -0
- package/src/prompt/prompt-generator.ts +8 -18
- package/src/scaffold/init.ts +17 -38
- package/src/scaffold/skill-adapters.ts +322 -0
- package/src/scaffold/templates.ts +207 -54
- package/src/sdd.ts +57 -31
- package/src/types.ts +2 -0
- package/tests/apply.test.ts +119 -0
- package/tests/integration.test.ts +94 -51
- package/dist/delta/hasher.d.ts +0 -2
- package/dist/delta/hasher.d.ts.map +0 -1
- package/dist/delta/hasher.js +0 -8
- package/dist/delta/hasher.js.map +0 -1
- package/dist/lock/lock-manager.d.ts +0 -6
- package/dist/lock/lock-manager.d.ts.map +0 -1
- package/dist/lock/lock-manager.js +0 -39
- package/dist/lock/lock-manager.js.map +0 -1
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import { describe, it, expect, beforeEach, afterEach } from
|
|
2
|
-
import { mkdtemp, rm, writeFile, readFile, mkdir } from
|
|
3
|
-
import { existsSync } from
|
|
4
|
-
import { tmpdir } from
|
|
5
|
-
import { join } from
|
|
6
|
-
import { execSync } from
|
|
7
|
-
import { SDD } from
|
|
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 { execSync } from "node:child_process";
|
|
7
|
+
import { SDD } from "../src/sdd.js";
|
|
8
8
|
|
|
9
9
|
function git(cmd: string, cwd: string): string {
|
|
10
|
-
return execSync(`git ${cmd}`, {
|
|
10
|
+
return execSync(`git ${cmd}`, {
|
|
11
|
+
cwd,
|
|
12
|
+
encoding: "utf-8",
|
|
13
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
14
|
+
}).trim();
|
|
11
15
|
}
|
|
12
16
|
|
|
13
17
|
const VISION_MD = `---
|
|
@@ -23,110 +27,149 @@ version: "1.0"
|
|
|
23
27
|
A test project.
|
|
24
28
|
`;
|
|
25
29
|
|
|
26
|
-
describe(
|
|
30
|
+
describe("SDD integration", () => {
|
|
27
31
|
let tempDir: string;
|
|
28
32
|
|
|
29
33
|
beforeEach(async () => {
|
|
30
|
-
tempDir = await mkdtemp(join(tmpdir(),
|
|
34
|
+
tempDir = await mkdtemp(join(tmpdir(), "sdd-integration-"));
|
|
31
35
|
});
|
|
32
36
|
|
|
33
37
|
afterEach(async () => {
|
|
34
38
|
await rm(tempDir, { recursive: true });
|
|
35
39
|
});
|
|
36
40
|
|
|
37
|
-
it(
|
|
41
|
+
it("init creates .sdd directory with config and git repo", async () => {
|
|
38
42
|
const sdd = new SDD({ root: tempDir });
|
|
39
43
|
|
|
40
|
-
const created = await sdd.init({ description:
|
|
41
|
-
expect(created).toContain(
|
|
42
|
-
expect(created).toContain(
|
|
43
|
-
expect(
|
|
44
|
-
expect(existsSync(join(tempDir,
|
|
45
|
-
expect(existsSync(join(tempDir,
|
|
46
|
-
expect(existsSync(join(tempDir,
|
|
47
|
-
expect(existsSync(join(tempDir,
|
|
44
|
+
const created = await sdd.init({ description: "A test app" });
|
|
45
|
+
expect(created).toContain(".sdd/config.yaml");
|
|
46
|
+
expect(created).toContain(".sdd/skill/sdd/SKILL.md");
|
|
47
|
+
expect(created).toContain(".claude/skills/sdd/SKILL.md");
|
|
48
|
+
expect(existsSync(join(tempDir, ".sdd"))).toBe(true);
|
|
49
|
+
expect(existsSync(join(tempDir, ".git"))).toBe(true);
|
|
50
|
+
expect(existsSync(join(tempDir, "product"))).toBe(true);
|
|
51
|
+
expect(existsSync(join(tempDir, "system"))).toBe(true);
|
|
52
|
+
expect(existsSync(join(tempDir, "code"))).toBe(true);
|
|
53
|
+
expect(existsSync(join(tempDir, ".sdd/skill/sdd/SKILL.md"))).toBe(true);
|
|
54
|
+
expect(
|
|
55
|
+
existsSync(join(tempDir, ".sdd/skill/sdd/references/file-format.md")),
|
|
56
|
+
).toBe(true);
|
|
57
|
+
expect(
|
|
58
|
+
existsSync(join(tempDir, ".sdd/skill/sdd/references/change-requests.md")),
|
|
59
|
+
).toBe(true);
|
|
60
|
+
expect(existsSync(join(tempDir, ".sdd/skill/sdd/references/bugs.md"))).toBe(
|
|
61
|
+
true,
|
|
62
|
+
);
|
|
63
|
+
expect(existsSync(join(tempDir, ".claude/skills/sdd/SKILL.md"))).toBe(true);
|
|
64
|
+
expect(
|
|
65
|
+
existsSync(join(tempDir, ".claude/skills/sdd/references/file-format.md")),
|
|
66
|
+
).toBe(true);
|
|
67
|
+
expect(
|
|
68
|
+
existsSync(
|
|
69
|
+
join(tempDir, ".claude/skills/sdd/references/change-requests.md"),
|
|
70
|
+
),
|
|
71
|
+
).toBe(true);
|
|
72
|
+
expect(
|
|
73
|
+
existsSync(join(tempDir, ".claude/skills/sdd/references/bugs.md")),
|
|
74
|
+
).toBe(true);
|
|
48
75
|
|
|
49
76
|
const config = await sdd.config();
|
|
50
|
-
expect(config.description).toBe(
|
|
77
|
+
expect(config.description).toBe("A test app");
|
|
51
78
|
});
|
|
52
79
|
|
|
53
|
-
it(
|
|
80
|
+
it("full workflow: init → add doc → status → sync → mark-synced", async () => {
|
|
54
81
|
const sdd = new SDD({ root: tempDir });
|
|
55
|
-
await sdd.init({ description:
|
|
82
|
+
await sdd.init({ description: "test" });
|
|
56
83
|
git('config user.email "test@test.com"', tempDir);
|
|
57
84
|
git('config user.name "Test"', tempDir);
|
|
58
85
|
|
|
59
86
|
// Simulate user creating a doc file
|
|
60
|
-
await writeFile(join(tempDir,
|
|
87
|
+
await writeFile(join(tempDir, "product/vision.md"), VISION_MD, "utf-8");
|
|
61
88
|
|
|
62
89
|
// Status — file should be "new"
|
|
63
90
|
const status = await sdd.status();
|
|
64
91
|
expect(status.files.length).toBe(1);
|
|
65
|
-
expect(status.files[0].status).toBe(
|
|
92
|
+
expect(status.files[0].status).toBe("new");
|
|
66
93
|
|
|
67
94
|
// Sync — prompt should list the new file
|
|
68
95
|
const prompt = await sdd.sync();
|
|
69
|
-
expect(prompt).toContain(
|
|
70
|
-
expect(prompt).toContain(
|
|
71
|
-
expect(prompt).toContain(
|
|
96
|
+
expect(prompt).toContain("# SDD Sync Prompt");
|
|
97
|
+
expect(prompt).toContain("product/vision.md");
|
|
98
|
+
expect(prompt).toContain("**new**");
|
|
72
99
|
|
|
73
100
|
// Validate
|
|
74
101
|
const validation = await sdd.validate();
|
|
75
102
|
expect(validation.valid).toBe(true);
|
|
76
103
|
|
|
77
104
|
// Mark synced — single file
|
|
78
|
-
const marked = await sdd.markSynced([
|
|
79
|
-
expect(marked).toContain(
|
|
105
|
+
const marked = await sdd.markSynced(["product/vision.md"]);
|
|
106
|
+
expect(marked).toContain("product/vision.md");
|
|
80
107
|
|
|
81
108
|
// Agent commits after mark-synced
|
|
82
|
-
git(
|
|
109
|
+
git("add .", tempDir);
|
|
83
110
|
git('commit -m "sync: vision"', tempDir);
|
|
84
111
|
|
|
85
112
|
// After commit, status should be synced
|
|
86
113
|
const statusAfter = await sdd.status();
|
|
87
|
-
expect(statusAfter.files[0].status).toBe(
|
|
114
|
+
expect(statusAfter.files[0].status).toBe("synced");
|
|
88
115
|
});
|
|
89
116
|
|
|
90
|
-
it(
|
|
117
|
+
it("mark-synced with changed status", async () => {
|
|
91
118
|
const sdd = new SDD({ root: tempDir });
|
|
92
|
-
await sdd.init({ description:
|
|
119
|
+
await sdd.init({ description: "test" });
|
|
93
120
|
git('config user.email "test@test.com"', tempDir);
|
|
94
121
|
git('config user.name "Test"', tempDir);
|
|
95
122
|
|
|
96
|
-
const changedMd = VISION_MD.replace(
|
|
97
|
-
await writeFile(join(tempDir,
|
|
123
|
+
const changedMd = VISION_MD.replace("status: new", "status: changed");
|
|
124
|
+
await writeFile(join(tempDir, "product/vision.md"), changedMd, "utf-8");
|
|
98
125
|
|
|
99
126
|
const marked = await sdd.markSynced();
|
|
100
|
-
expect(marked).toContain(
|
|
127
|
+
expect(marked).toContain("product/vision.md");
|
|
101
128
|
|
|
102
|
-
const content = await readFile(join(tempDir,
|
|
103
|
-
expect(content).toContain(
|
|
129
|
+
const content = await readFile(join(tempDir, "product/vision.md"), "utf-8");
|
|
130
|
+
expect(content).toContain("status: synced");
|
|
104
131
|
});
|
|
105
132
|
|
|
106
|
-
it(
|
|
133
|
+
it("mark-synced with deleted status removes the file", async () => {
|
|
107
134
|
const sdd = new SDD({ root: tempDir });
|
|
108
|
-
await sdd.init({ description:
|
|
135
|
+
await sdd.init({ description: "test" });
|
|
109
136
|
git('config user.email "test@test.com"', tempDir);
|
|
110
137
|
git('config user.name "Test"', tempDir);
|
|
111
138
|
|
|
112
|
-
const deletedMd = VISION_MD.replace(
|
|
113
|
-
await writeFile(join(tempDir,
|
|
139
|
+
const deletedMd = VISION_MD.replace("status: new", "status: deleted");
|
|
140
|
+
await writeFile(join(tempDir, "product/vision.md"), deletedMd, "utf-8");
|
|
114
141
|
|
|
115
142
|
const marked = await sdd.markSynced();
|
|
116
|
-
expect(marked[0]).toContain(
|
|
117
|
-
expect(existsSync(join(tempDir,
|
|
143
|
+
expect(marked[0]).toContain("removed");
|
|
144
|
+
expect(existsSync(join(tempDir, "product/vision.md"))).toBe(false);
|
|
118
145
|
});
|
|
119
146
|
|
|
120
|
-
it(
|
|
147
|
+
it("throws when project not initialized", async () => {
|
|
121
148
|
const sdd = new SDD({ root: tempDir });
|
|
122
|
-
await expect(sdd.status()).rejects.toThrow(
|
|
149
|
+
await expect(sdd.status()).rejects.toThrow("No SDD project found");
|
|
123
150
|
});
|
|
124
151
|
|
|
125
|
-
it(
|
|
152
|
+
it("init is idempotent for canonical and adapter skill files", async () => {
|
|
126
153
|
const sdd = new SDD({ root: tempDir });
|
|
127
|
-
const first = await sdd.init({ description:
|
|
128
|
-
const second = await sdd.init({ description:
|
|
129
|
-
expect(first).toContain(
|
|
130
|
-
expect(
|
|
154
|
+
const first = await sdd.init({ description: "test" });
|
|
155
|
+
const second = await sdd.init({ description: "test" });
|
|
156
|
+
expect(first).toContain(".sdd/skill/sdd/SKILL.md");
|
|
157
|
+
expect(first).toContain(".claude/skills/sdd/SKILL.md");
|
|
158
|
+
expect(second).not.toContain(".sdd/skill/sdd/SKILL.md");
|
|
159
|
+
expect(second).not.toContain(".claude/skills/sdd/SKILL.md");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("syncAdapters supports dry-run and selective adapters", async () => {
|
|
163
|
+
const sdd = new SDD({ root: tempDir });
|
|
164
|
+
await sdd.init({ description: "test" });
|
|
165
|
+
|
|
166
|
+
const dryRun = await sdd.syncAdapters({ agents: ["claude"], dryRun: true });
|
|
167
|
+
expect(dryRun.selectedAgents).toEqual(["claude"]);
|
|
168
|
+
expect(
|
|
169
|
+
dryRun.adapters.some((c) => c.path === ".claude/skills/sdd/SKILL.md"),
|
|
170
|
+
).toBe(true);
|
|
171
|
+
|
|
172
|
+
await sdd.syncAdapters({ agents: ["claude"] });
|
|
173
|
+
expect(existsSync(join(tempDir, ".claude/skills/sdd/SKILL.md"))).toBe(true);
|
|
131
174
|
});
|
|
132
175
|
});
|
package/dist/delta/hasher.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hasher.d.ts","sourceRoot":"","sources":["../../src/delta/hasher.ts"],"names":[],"mappings":"AAEA,wBAAgB,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
package/dist/delta/hasher.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.sha256 = sha256;
|
|
4
|
-
const node_crypto_1 = require("node:crypto");
|
|
5
|
-
function sha256(content) {
|
|
6
|
-
return (0, node_crypto_1.createHash)('sha256').update(content).digest('hex');
|
|
7
|
-
}
|
|
8
|
-
//# sourceMappingURL=hasher.js.map
|
package/dist/delta/hasher.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hasher.js","sourceRoot":"","sources":["../../src/delta/hasher.ts"],"names":[],"mappings":";;AAEA,wBAEC;AAJD,6CAAyC;AAEzC,SAAgB,MAAM,CAAC,OAAe;IACpC,OAAO,IAAA,wBAAU,EAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC5D,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { LockFile } from '../types.js';
|
|
2
|
-
export declare function lockFilePath(root: string): string;
|
|
3
|
-
export declare function readLockFile(root: string): Promise<LockFile>;
|
|
4
|
-
export declare function writeLockFile(root: string, lock: LockFile): Promise<void>;
|
|
5
|
-
export declare function createEmptyLock(): LockFile;
|
|
6
|
-
//# sourceMappingURL=lock-manager.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock-manager.d.ts","sourceRoot":"","sources":["../../src/lock/lock-manager.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAK5C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQlE;AAED,wBAAsB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/E;AAED,wBAAgB,eAAe,IAAI,QAAQ,CAK1C"}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.lockFilePath = lockFilePath;
|
|
7
|
-
exports.readLockFile = readLockFile;
|
|
8
|
-
exports.writeLockFile = writeLockFile;
|
|
9
|
-
exports.createEmptyLock = createEmptyLock;
|
|
10
|
-
const promises_1 = require("node:fs/promises");
|
|
11
|
-
const node_fs_1 = require("node:fs");
|
|
12
|
-
const node_path_1 = require("node:path");
|
|
13
|
-
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
14
|
-
const config_manager_js_1 = require("../config/config-manager.js");
|
|
15
|
-
const LOCK_FILENAME = 'lock.yaml';
|
|
16
|
-
function lockFilePath(root) {
|
|
17
|
-
return (0, node_path_1.resolve)(root, config_manager_js_1.SDD_DIR, LOCK_FILENAME);
|
|
18
|
-
}
|
|
19
|
-
async function readLockFile(root) {
|
|
20
|
-
const path = lockFilePath(root);
|
|
21
|
-
if (!(0, node_fs_1.existsSync)(path)) {
|
|
22
|
-
return createEmptyLock();
|
|
23
|
-
}
|
|
24
|
-
const content = await (0, promises_1.readFile)(path, 'utf-8');
|
|
25
|
-
const parsed = js_yaml_1.default.load(content);
|
|
26
|
-
return parsed ?? createEmptyLock();
|
|
27
|
-
}
|
|
28
|
-
async function writeLockFile(root, lock) {
|
|
29
|
-
const path = lockFilePath(root);
|
|
30
|
-
const content = js_yaml_1.default.dump(lock, { sortKeys: true, lineWidth: -1 });
|
|
31
|
-
await (0, promises_1.writeFile)(path, content, 'utf-8');
|
|
32
|
-
}
|
|
33
|
-
function createEmptyLock() {
|
|
34
|
-
return {
|
|
35
|
-
'synced-at': new Date().toISOString(),
|
|
36
|
-
files: {},
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
//# sourceMappingURL=lock-manager.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"lock-manager.js","sourceRoot":"","sources":["../../src/lock/lock-manager.ts"],"names":[],"mappings":";;;;;AASA,oCAEC;AAED,oCAQC;AAED,sCAIC;AAED,0CAKC;AAlCD,+CAAuD;AACvD,qCAAqC;AACrC,yCAAoC;AACpC,sDAA2B;AAE3B,mEAAsD;AAEtD,MAAM,aAAa,GAAG,WAAW,CAAC;AAElC,SAAgB,YAAY,CAAC,IAAY;IACvC,OAAO,IAAA,mBAAO,EAAC,IAAI,EAAE,2BAAO,EAAE,aAAa,CAAC,CAAC;AAC/C,CAAC;AAEM,KAAK,UAAU,YAAY,CAAC,IAAY;IAC7C,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,IAAA,oBAAU,EAAC,IAAI,CAAC,EAAE,CAAC;QACtB,OAAO,eAAe,EAAE,CAAC;IAC3B,CAAC;IACD,MAAM,OAAO,GAAG,MAAM,IAAA,mBAAQ,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAG,iBAAI,CAAC,IAAI,CAAC,OAAO,CAAoB,CAAC;IACrD,OAAO,MAAM,IAAI,eAAe,EAAE,CAAC;AACrC,CAAC;AAEM,KAAK,UAAU,aAAa,CAAC,IAAY,EAAE,IAAc;IAC9D,MAAM,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,OAAO,GAAG,iBAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;IACnE,MAAM,IAAA,oBAAS,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAC1C,CAAC;AAED,SAAgB,eAAe;IAC7B,OAAO;QACL,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACrC,KAAK,EAAE,EAAE;KACV,CAAC;AACJ,CAAC"}
|