contract-driven-delivery 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 (97) hide show
  1. package/README.md +142 -0
  2. package/assets/AGENTS.template.md +21 -0
  3. package/assets/CLAUDE.template.md +159 -0
  4. package/assets/agents/backend-engineer.md +24 -0
  5. package/assets/agents/change-classifier.md +73 -0
  6. package/assets/agents/ci-cd-gatekeeper.md +40 -0
  7. package/assets/agents/contract-reviewer.md +42 -0
  8. package/assets/agents/e2e-resilience-engineer.md +25 -0
  9. package/assets/agents/frontend-engineer.md +22 -0
  10. package/assets/agents/monkey-test-engineer.md +29 -0
  11. package/assets/agents/qa-reviewer.md +49 -0
  12. package/assets/agents/repo-context-scanner.md +71 -0
  13. package/assets/agents/spec-architect.md +47 -0
  14. package/assets/agents/spec-drift-auditor.md +41 -0
  15. package/assets/agents/stress-soak-engineer.md +51 -0
  16. package/assets/agents/test-strategist.md +57 -0
  17. package/assets/agents/ui-ux-reviewer.md +42 -0
  18. package/assets/agents/visual-reviewer.md +44 -0
  19. package/assets/ci/gate-policy.md +51 -0
  20. package/assets/ci/github-actions/contract-driven-gates.yml +38 -0
  21. package/assets/ci/required-check-policy.md +13 -0
  22. package/assets/contracts/api/api-contract.md +20 -0
  23. package/assets/contracts/api/api-inventory.md +13 -0
  24. package/assets/contracts/api/error-format.md +19 -0
  25. package/assets/contracts/business/business-rules.md +13 -0
  26. package/assets/contracts/ci/ci-gate-contract.md +13 -0
  27. package/assets/contracts/css/css-contract.md +15 -0
  28. package/assets/contracts/css/design-tokens.md +13 -0
  29. package/assets/contracts/data/data-shape-contract.md +22 -0
  30. package/assets/contracts/env/.env.example.template +5 -0
  31. package/assets/contracts/env/env-contract.md +12 -0
  32. package/assets/contracts/env/env.schema.json +7 -0
  33. package/assets/skill/SKILL.md +102 -0
  34. package/assets/skill/agents/openai.yaml +2 -0
  35. package/assets/skill/references/api-contract-standard.md +55 -0
  36. package/assets/skill/references/business-logic-standard.md +32 -0
  37. package/assets/skill/references/ci-cd-policy.md +34 -0
  38. package/assets/skill/references/css-contract-standard.md +40 -0
  39. package/assets/skill/references/data-contract-standard.md +43 -0
  40. package/assets/skill/references/e2e-standard.md +33 -0
  41. package/assets/skill/references/env-contract-standard.md +30 -0
  42. package/assets/skill/references/monkey-operation-standard.md +32 -0
  43. package/assets/skill/references/qa-gates.md +37 -0
  44. package/assets/skill/references/sdd-tdd-policy.md +53 -0
  45. package/assets/skill/references/spec-drift-policy.md +28 -0
  46. package/assets/skill/references/stress-soak-standard.md +42 -0
  47. package/assets/skill/references/visual-review-standard.md +27 -0
  48. package/assets/skill/references/workflow-router.md +34 -0
  49. package/assets/skill/scripts/detect_project_profile.py +61 -0
  50. package/assets/skill/scripts/generate_change_scaffold.py +38 -0
  51. package/assets/skill/scripts/validate_ci_gates.py +14 -0
  52. package/assets/skill/scripts/validate_contracts.py +13 -0
  53. package/assets/skill/scripts/validate_env_contract.py +16 -0
  54. package/assets/skill/scripts/validate_spec_traceability.py +18 -0
  55. package/assets/skill/templates/archive.md +17 -0
  56. package/assets/skill/templates/change-classification.md +48 -0
  57. package/assets/skill/templates/change-request.md +15 -0
  58. package/assets/skill/templates/ci-gates.md +31 -0
  59. package/assets/skill/templates/contracts.md +37 -0
  60. package/assets/skill/templates/current-behavior.md +17 -0
  61. package/assets/skill/templates/design.md +23 -0
  62. package/assets/skill/templates/monkey-test-report.md +17 -0
  63. package/assets/skill/templates/project-profile.md +38 -0
  64. package/assets/skill/templates/proposal.md +17 -0
  65. package/assets/skill/templates/qa-report.md +26 -0
  66. package/assets/skill/templates/regression-report.md +21 -0
  67. package/assets/skill/templates/spec.md +23 -0
  68. package/assets/skill/templates/stress-soak-report.md +22 -0
  69. package/assets/skill/templates/tasks.md +43 -0
  70. package/assets/skill/templates/test-plan.md +31 -0
  71. package/assets/skill/templates/visual-review-report.md +33 -0
  72. package/assets/specs-templates/archive.md +17 -0
  73. package/assets/specs-templates/change-classification.md +48 -0
  74. package/assets/specs-templates/change-request.md +15 -0
  75. package/assets/specs-templates/ci-gates.md +31 -0
  76. package/assets/specs-templates/contracts.md +37 -0
  77. package/assets/specs-templates/current-behavior.md +17 -0
  78. package/assets/specs-templates/design.md +23 -0
  79. package/assets/specs-templates/monkey-test-report.md +17 -0
  80. package/assets/specs-templates/project-profile.md +38 -0
  81. package/assets/specs-templates/proposal.md +17 -0
  82. package/assets/specs-templates/qa-report.md +26 -0
  83. package/assets/specs-templates/regression-report.md +21 -0
  84. package/assets/specs-templates/spec.md +23 -0
  85. package/assets/specs-templates/stress-soak-report.md +22 -0
  86. package/assets/specs-templates/tasks.md +43 -0
  87. package/assets/specs-templates/test-plan.md +31 -0
  88. package/assets/specs-templates/visual-review-report.md +33 -0
  89. package/assets/tests-templates/data-boundary/malformed-data.spec.md +14 -0
  90. package/assets/tests-templates/e2e/critical-journey.spec.md +13 -0
  91. package/assets/tests-templates/monkey/operation-sequence.spec.md +11 -0
  92. package/assets/tests-templates/resilience/api-failure.spec.md +12 -0
  93. package/assets/tests-templates/soak/soak-profile.md +15 -0
  94. package/assets/tests-templates/stress/load-profile.md +15 -0
  95. package/bin/cdd.js +2 -0
  96. package/dist/cli/index.js +336 -0
  97. package/package.json +48 -0
@@ -0,0 +1,336 @@
1
+ // src/cli/index.ts
2
+ import { Command } from "commander";
3
+
4
+ // src/commands/init.ts
5
+ import { join as join3 } from "path";
6
+
7
+ // src/utils/paths.ts
8
+ import { join, dirname } from "path";
9
+ import { fileURLToPath } from "url";
10
+ import { homedir } from "os";
11
+ var __dirname = dirname(fileURLToPath(import.meta.url));
12
+ var PACKAGE_ROOT = join(__dirname, "..", "..");
13
+ var ASSETS_DIR = join(PACKAGE_ROOT, "assets");
14
+ var CLAUDE_HOME = join(homedir(), ".claude");
15
+ var AGENTS_HOME = join(CLAUDE_HOME, "agents");
16
+ var SKILLS_HOME = join(CLAUDE_HOME, "skills");
17
+ var ASSET = {
18
+ agents: join(ASSETS_DIR, "agents"),
19
+ skill: join(ASSETS_DIR, "skill"),
20
+ contracts: join(ASSETS_DIR, "contracts"),
21
+ specsTemplates: join(ASSETS_DIR, "specs-templates"),
22
+ testsTemplates: join(ASSETS_DIR, "tests-templates"),
23
+ ci: join(ASSETS_DIR, "ci"),
24
+ claudeTemplate: join(ASSETS_DIR, "CLAUDE.template.md"),
25
+ agentsTemplate: join(ASSETS_DIR, "AGENTS.template.md")
26
+ };
27
+
28
+ // src/utils/copy.ts
29
+ import {
30
+ mkdirSync,
31
+ existsSync,
32
+ readdirSync,
33
+ copyFileSync
34
+ } from "fs";
35
+ import { join as join2, dirname as dirname2, relative } from "path";
36
+
37
+ // src/utils/logger.ts
38
+ var RESET = "\x1B[0m";
39
+ var CYAN = "\x1B[36m";
40
+ var GREEN = "\x1B[32m";
41
+ var YELLOW = "\x1B[33m";
42
+ var RED = "\x1B[31m";
43
+ var DIM = "\x1B[2m";
44
+ var log = {
45
+ info(msg) {
46
+ console.log(`${CYAN}\u2139${RESET} ${msg}`);
47
+ },
48
+ ok(msg) {
49
+ console.log(`${GREEN}\u2713${RESET} ${msg}`);
50
+ },
51
+ warn(msg) {
52
+ console.log(`${YELLOW}\u26A0${RESET} ${msg}`);
53
+ },
54
+ error(msg) {
55
+ console.error(`${RED}\u2717${RESET} ${msg}`);
56
+ },
57
+ dim(msg) {
58
+ console.log(`${DIM} ${msg}${RESET}`);
59
+ },
60
+ blank() {
61
+ console.log("");
62
+ }
63
+ };
64
+
65
+ // src/utils/copy.ts
66
+ function ensureDir(dir) {
67
+ mkdirSync(dir, { recursive: true });
68
+ }
69
+ function copyDir(src, dest, opts = {}) {
70
+ const { overwrite = true, label } = opts;
71
+ if (!existsSync(src)) {
72
+ log.warn(`source not found, skipping: ${label ?? src}`);
73
+ return 0;
74
+ }
75
+ ensureDir(dest);
76
+ let count = 0;
77
+ function walk(currentSrc, currentDest) {
78
+ const entries = readdirSync(currentSrc, { withFileTypes: true });
79
+ for (const entry of entries) {
80
+ const srcPath = join2(currentSrc, entry.name);
81
+ const destPath = join2(currentDest, entry.name);
82
+ if (entry.isDirectory()) {
83
+ ensureDir(destPath);
84
+ walk(srcPath, destPath);
85
+ } else {
86
+ if (!overwrite && existsSync(destPath)) {
87
+ const relPath = label ? join2(label, relative(dest, destPath)) : relative(dest, destPath);
88
+ log.dim(`skip ${relPath}`);
89
+ continue;
90
+ }
91
+ ensureDir(dirname2(destPath));
92
+ copyFileSync(srcPath, destPath);
93
+ count += 1;
94
+ }
95
+ }
96
+ }
97
+ walk(src, dest);
98
+ return count;
99
+ }
100
+ function copyFile(src, dest, opts = {}) {
101
+ const { overwrite = true, label } = opts;
102
+ if (!overwrite && existsSync(dest)) {
103
+ log.dim(`skip ${label ?? dest}`);
104
+ return false;
105
+ }
106
+ ensureDir(dirname2(dest));
107
+ copyFileSync(src, dest);
108
+ return true;
109
+ }
110
+
111
+ // src/commands/init.ts
112
+ async function init(opts) {
113
+ if (opts.globalOnly && opts.localOnly) {
114
+ log.error("--global-only and --local-only are mutually exclusive.");
115
+ process.exit(1);
116
+ }
117
+ const cwd = process.cwd();
118
+ log.blank();
119
+ log.info("Initialising contract-driven-delivery kit\u2026");
120
+ log.blank();
121
+ if (!opts.localOnly) {
122
+ log.info(`Installing agents \u2192 ${AGENTS_HOME}`);
123
+ const agentCount = copyDir(ASSET.agents, AGENTS_HOME, { overwrite: true });
124
+ log.ok(`${agentCount} agent file(s) installed.`);
125
+ const skillDest = join3(SKILLS_HOME, "contract-driven-delivery");
126
+ log.info(`Installing skill \u2192 ${skillDest}`);
127
+ const skillCount = copyDir(ASSET.skill, skillDest, { overwrite: true });
128
+ log.ok(`${skillCount} skill file(s) installed.`);
129
+ log.blank();
130
+ }
131
+ if (!opts.globalOnly) {
132
+ log.info(`Scaffolding project files in ${cwd}`);
133
+ const contractsCount = copyDir(
134
+ ASSET.contracts,
135
+ join3(cwd, "contracts"),
136
+ { overwrite: opts.force, label: "contracts" }
137
+ );
138
+ log.ok(`contracts/ \u2014 ${contractsCount} file(s) written.`);
139
+ const specsCount = copyDir(
140
+ ASSET.specsTemplates,
141
+ join3(cwd, "specs", "templates"),
142
+ { overwrite: opts.force, label: "specs/templates" }
143
+ );
144
+ log.ok(`specs/templates/ \u2014 ${specsCount} file(s) written.`);
145
+ const testsCount = copyDir(
146
+ ASSET.testsTemplates,
147
+ join3(cwd, "tests", "templates"),
148
+ { overwrite: opts.force, label: "tests/templates" }
149
+ );
150
+ log.ok(`tests/templates/ \u2014 ${testsCount} file(s) written.`);
151
+ const ciCount = copyDir(
152
+ ASSET.ci,
153
+ join3(cwd, "ci"),
154
+ { overwrite: opts.force, label: "ci" }
155
+ );
156
+ log.ok(`ci/ \u2014 ${ciCount} file(s) written.`);
157
+ const claudeWritten = copyFile(
158
+ ASSET.claudeTemplate,
159
+ join3(cwd, "CLAUDE.md"),
160
+ { overwrite: false, label: "CLAUDE.md" }
161
+ );
162
+ if (claudeWritten)
163
+ log.ok("CLAUDE.md created.");
164
+ const agentsWritten = copyFile(
165
+ ASSET.agentsTemplate,
166
+ join3(cwd, "AGENTS.md"),
167
+ { overwrite: false, label: "AGENTS.md" }
168
+ );
169
+ if (agentsWritten)
170
+ log.ok("AGENTS.md created.");
171
+ log.blank();
172
+ }
173
+ log.ok("Done.");
174
+ log.blank();
175
+ log.info("Use the contract-driven-delivery skill in Claude Code to scan this repo.");
176
+ log.blank();
177
+ }
178
+
179
+ // src/commands/update.ts
180
+ import { join as join4 } from "path";
181
+ async function update() {
182
+ log.blank();
183
+ log.info("Updating ~/.claude agents and skill\u2026");
184
+ log.blank();
185
+ log.info(`Updating agents \u2192 ${AGENTS_HOME}`);
186
+ const agentCount = copyDir(ASSET.agents, AGENTS_HOME, { overwrite: true });
187
+ log.ok(`${agentCount} agent file(s) updated.`);
188
+ const skillDest = join4(SKILLS_HOME, "contract-driven-delivery");
189
+ log.info(`Updating skill \u2192 ${skillDest}`);
190
+ const skillCount = copyDir(ASSET.skill, skillDest, { overwrite: true });
191
+ log.ok(`${skillCount} skill file(s) updated.`);
192
+ log.blank();
193
+ log.info("Project files (contracts/, specs/, tests/, ci/) were not changed.");
194
+ log.ok("Update complete.");
195
+ log.blank();
196
+ }
197
+
198
+ // src/commands/new-change.ts
199
+ import { join as join5 } from "path";
200
+ import { existsSync as existsSync2 } from "fs";
201
+ var REQUIRED_TEMPLATES = [
202
+ "change-request.md",
203
+ "change-classification.md",
204
+ "test-plan.md",
205
+ "ci-gates.md",
206
+ "tasks.md"
207
+ ];
208
+ var OPTIONAL_TEMPLATES = [
209
+ "current-behavior.md",
210
+ "proposal.md",
211
+ "spec.md",
212
+ "design.md",
213
+ "contracts.md",
214
+ "qa-report.md",
215
+ "regression-report.md",
216
+ "archive.md"
217
+ ];
218
+ var SAFE_NAME = /^[a-z0-9][a-z0-9_-]{0,63}$/i;
219
+ async function newChange(name, opts) {
220
+ if (!SAFE_NAME.test(name)) {
221
+ log.error(`Invalid change name: "${name}". Use letters, numbers, hyphens, or underscores (max 64 chars).`);
222
+ process.exit(1);
223
+ }
224
+ const cwd = process.cwd();
225
+ const changeDir = join5(cwd, "specs", "changes", name);
226
+ if (existsSync2(changeDir)) {
227
+ log.warn(`Change directory already exists: ${changeDir}`);
228
+ log.warn("Aborting \u2014 remove or rename the directory to re-scaffold.");
229
+ return;
230
+ }
231
+ log.blank();
232
+ log.info(`Creating change scaffold: specs/changes/${name}`);
233
+ ensureDir(changeDir);
234
+ const templates = opts.all ? [...REQUIRED_TEMPLATES, ...OPTIONAL_TEMPLATES] : [...REQUIRED_TEMPLATES];
235
+ let written = 0;
236
+ for (const tmpl of templates) {
237
+ const src = join5(ASSET.specsTemplates, tmpl);
238
+ const dest = join5(changeDir, tmpl);
239
+ if (!existsSync2(src)) {
240
+ log.warn(`Template not found, skipping: ${tmpl}`);
241
+ continue;
242
+ }
243
+ copyFile(src, dest, { overwrite: false });
244
+ log.dim(tmpl);
245
+ written += 1;
246
+ }
247
+ log.blank();
248
+ log.ok(`${written} template(s) created in specs/changes/${name}`);
249
+ log.blank();
250
+ }
251
+
252
+ // src/commands/validate.ts
253
+ import { join as join6 } from "path";
254
+ import { existsSync as existsSync3 } from "fs";
255
+ import { execSync } from "child_process";
256
+ var VALIDATORS = [
257
+ { flag: "contracts", script: "validate_contracts.py", label: "contracts" },
258
+ { flag: "env", script: "validate_env_contract.py", label: "env contract" },
259
+ { flag: "ci", script: "validate_ci_gates.py", label: "CI gates" },
260
+ { flag: "spec", script: "validate_spec_traceability.py", label: "spec traceability" }
261
+ ];
262
+ function resolvePython() {
263
+ for (const cmd of ["python3", "python"]) {
264
+ try {
265
+ execSync(`${cmd} --version`, { stdio: "ignore" });
266
+ return cmd;
267
+ } catch {
268
+ }
269
+ }
270
+ throw new Error("Python not found. Install Python 3.8+ and ensure it is on PATH.");
271
+ }
272
+ async function validate(opts) {
273
+ let py;
274
+ try {
275
+ py = resolvePython();
276
+ } catch (e) {
277
+ log.error(e instanceof Error ? e.message : String(e));
278
+ process.exit(1);
279
+ }
280
+ const scriptsDir = join6(ASSET.skill, "scripts");
281
+ const runAll = !opts.contracts && !opts.env && !opts.ci && !opts.spec;
282
+ log.blank();
283
+ let failed = false;
284
+ for (const v of VALIDATORS) {
285
+ if (!runAll && !opts[v.flag])
286
+ continue;
287
+ const scriptPath = join6(scriptsDir, v.script);
288
+ if (!existsSync3(scriptPath)) {
289
+ log.warn(`${v.label}: script not found, skipping (${v.script})`);
290
+ log.blank();
291
+ continue;
292
+ }
293
+ log.info(`Validating ${v.label}\u2026`);
294
+ try {
295
+ execSync(`${py} "${scriptPath}"`, { stdio: "inherit", cwd: process.cwd() });
296
+ log.ok(`${v.label} passed.`);
297
+ } catch {
298
+ log.error(`${v.label} validation failed.`);
299
+ failed = true;
300
+ }
301
+ log.blank();
302
+ }
303
+ if (failed) {
304
+ log.error("One or more validations failed.");
305
+ process.exit(1);
306
+ } else {
307
+ log.ok("All validations passed.");
308
+ log.blank();
309
+ }
310
+ }
311
+
312
+ // src/cli/index.ts
313
+ var program = new Command();
314
+ program.name("cdd").description("Contract-Driven Delivery Kit CLI").version("1.0.0");
315
+ program.command("init").description(
316
+ "Install agents/skill into ~/.claude and scaffold project files in cwd"
317
+ ).option("--global-only", "Only install into ~/.claude, skip project files", false).option("--local-only", "Only scaffold project files, skip ~/.claude", false).option("--force", "Overwrite existing project files", false).action(
318
+ (opts) => init({
319
+ globalOnly: opts.globalOnly,
320
+ localOnly: opts.localOnly,
321
+ force: opts.force
322
+ })
323
+ );
324
+ program.command("update").description("Update ~/.claude agents and skill (does not touch project files)").action(() => update());
325
+ program.command("new <name>").description("Scaffold a new change directory under specs/changes/<name>").option("--all", "Include optional templates in addition to required ones", false).action(
326
+ (name, opts) => newChange(name, { all: opts.all })
327
+ );
328
+ program.command("validate").description("Run validation scripts (defaults to all)").option("--contracts", "Validate API/data/CSS contracts (use --env separately for env)", false).option("--env", "Validate env contract", false).option("--ci", "Validate CI gate policy", false).option("--spec", "Validate spec traceability", false).action(
329
+ (opts) => validate({
330
+ contracts: opts.contracts,
331
+ env: opts.env,
332
+ ci: opts.ci,
333
+ spec: opts.spec
334
+ })
335
+ );
336
+ program.parse();
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "contract-driven-delivery",
3
+ "version": "1.0.0",
4
+ "description": "Contract-driven delivery kit CLI — spec-first, test-first AI-assisted delivery for brownfield systems",
5
+ "keywords": [
6
+ "contract-driven",
7
+ "sdd",
8
+ "tdd",
9
+ "claude-code",
10
+ "ai",
11
+ "specs",
12
+ "contracts"
13
+ ],
14
+ "license": "MIT",
15
+ "author": "Contract-Driven Delivery Contributors",
16
+ "type": "module",
17
+ "publishConfig": {
18
+ "access": "public"
19
+ },
20
+ "bin": {
21
+ "cdd": "bin/cdd.js"
22
+ },
23
+ "exports": {
24
+ ".": {
25
+ "default": "./dist/cli/index.js"
26
+ }
27
+ },
28
+ "files": [
29
+ "dist",
30
+ "bin",
31
+ "assets"
32
+ ],
33
+ "scripts": {
34
+ "build": "node build.js",
35
+ "prepublishOnly": "node build.js"
36
+ },
37
+ "engines": {
38
+ "node": ">=18.0.0"
39
+ },
40
+ "dependencies": {
41
+ "commander": "^12.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.0.0",
45
+ "esbuild": "^0.20.0",
46
+ "typescript": "^5.0.0"
47
+ }
48
+ }