id3-cli 0.9.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/README.ja-JP.md +769 -0
- package/README.ko-KR.md +769 -0
- package/README.md +769 -0
- package/README.tr-TR.md +769 -0
- package/README.zh-CN.md +769 -0
- package/dist/bin/cli.d.ts +2 -0
- package/dist/bin/cli.js +40 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/scripts/build-hooks.d.ts +1 -0
- package/dist/scripts/build-hooks.js +58 -0
- package/dist/scripts/build-hooks.js.map +1 -0
- package/dist/src/hooks/auto-audit.d.ts +4 -0
- package/dist/src/hooks/auto-audit.js +47 -0
- package/dist/src/hooks/auto-audit.js.map +1 -0
- package/dist/src/hooks/claude-pretool-entry.d.ts +1 -0
- package/dist/src/hooks/claude-pretool-entry.js +36 -0
- package/dist/src/hooks/claude-pretool-entry.js.map +1 -0
- package/dist/src/hooks/claude-stop-entry.d.ts +1 -0
- package/dist/src/hooks/claude-stop-entry.js +7 -0
- package/dist/src/hooks/claude-stop-entry.js.map +1 -0
- package/dist/src/hooks/post-commit-entry.d.ts +1 -0
- package/dist/src/hooks/post-commit-entry.js +7 -0
- package/dist/src/hooks/post-commit-entry.js.map +1 -0
- package/dist/src/hooks/pre-commit-entry.d.ts +1 -0
- package/dist/src/hooks/pre-commit-entry.js +16 -0
- package/dist/src/hooks/pre-commit-entry.js.map +1 -0
- package/dist/src/hooks/rule-check.d.ts +8 -0
- package/dist/src/hooks/rule-check.js +101 -0
- package/dist/src/hooks/rule-check.js.map +1 -0
- package/dist/src/hooks/schema-drift.d.ts +17 -0
- package/dist/src/hooks/schema-drift.js +151 -0
- package/dist/src/hooks/schema-drift.js.map +1 -0
- package/dist/src/hooks/shared.d.ts +43 -0
- package/dist/src/hooks/shared.js +98 -0
- package/dist/src/hooks/shared.js.map +1 -0
- package/dist/src/init.d.ts +20 -0
- package/dist/src/init.js +193 -0
- package/dist/src/init.js.map +1 -0
- package/dist/src/preview/mockup-generator.d.ts +56 -0
- package/dist/src/preview/mockup-generator.js +402 -0
- package/dist/src/preview/mockup-generator.js.map +1 -0
- package/dist/src/preview/renderer.d.ts +30 -0
- package/dist/src/preview/renderer.js +145 -0
- package/dist/src/preview/renderer.js.map +1 -0
- package/dist/src/preview/server.d.ts +9 -0
- package/dist/src/preview/server.js +55 -0
- package/dist/src/preview/server.js.map +1 -0
- package/dist/src/preview/ui-auditor.d.ts +27 -0
- package/dist/src/preview/ui-auditor.js +141 -0
- package/dist/src/preview/ui-auditor.js.map +1 -0
- package/dist/src/preview/ui-gate.d.ts +66 -0
- package/dist/src/preview/ui-gate.js +210 -0
- package/dist/src/preview/ui-gate.js.map +1 -0
- package/dist/src/utils/ascii.d.ts +7 -0
- package/dist/src/utils/ascii.js +41 -0
- package/dist/src/utils/ascii.js.map +1 -0
- package/dist/src/utils/fs.d.ts +6 -0
- package/dist/src/utils/fs.js +39 -0
- package/dist/src/utils/fs.js.map +1 -0
- package/dist/templates/hooks/iddd-auto-audit.js +121 -0
- package/dist/templates/hooks/iddd-schema-drift.js +279 -0
- package/dist/templates/hooks/post-commit +121 -0
- package/dist/templates/hooks/pre-commit +348 -0
- package/package.json +37 -0
- package/templates/.agents/skills/.gitkeep +0 -0
- package/templates/.claude/hooks/.gitkeep +0 -0
- package/templates/.claude/hooks/hook-config.json +34 -0
- package/templates/.claude/skills/.gitkeep +0 -0
- package/templates/.codex/.gitkeep +0 -0
- package/templates/.codex/hooks.json +40 -0
- package/templates/.iddd/commit-count +1 -0
- package/templates/.iddd/preview/.gitkeep +0 -0
- package/templates/AGENTS.md +204 -0
- package/templates/CLAUDE.md +215 -0
- package/templates/README.md +476 -0
- package/templates/docs/.gitkeep +0 -0
- package/templates/docs/business-rules.md +14 -0
- package/templates/docs/domain-glossary.md +8 -0
- package/templates/docs/info-debt.md +17 -0
- package/templates/docs/model-changelog.md +12 -0
- package/templates/hooks/.gitkeep +0 -0
- package/templates/hooks/iddd-auto-audit.js +121 -0
- package/templates/hooks/iddd-schema-drift.js +279 -0
- package/templates/hooks/post-commit +121 -0
- package/templates/hooks/pre-commit +348 -0
- package/templates/skills/id3-design-information/SKILL.md +170 -0
- package/templates/skills/id3-design-information/references/phase2-procedure.md +241 -0
- package/templates/skills/id3-design-ui/.gitkeep +0 -0
- package/templates/skills/id3-design-ui/SKILL.md +200 -0
- package/templates/skills/id3-design-ui/references/.gitkeep +0 -0
- package/templates/skills/id3-design-ui/references/step1-structure-derivation.md +177 -0
- package/templates/skills/id3-design-ui/references/step2-visual-contract.md +257 -0
- package/templates/skills/id3-design-ui/references/step3-gate-and-mockup.md +177 -0
- package/templates/skills/id3-design-ui/references/step4-implementation.md +244 -0
- package/templates/skills/id3-identify-entities/SKILL.md +239 -0
- package/templates/skills/id3-identify-entities/references/.gitkeep +0 -0
- package/templates/skills/id3-identify-entities/references/phase0-brownfield.md +377 -0
- package/templates/skills/id3-identify-entities/references/phase1-greenfield.md +319 -0
- package/templates/skills/id3-info-audit/.gitkeep +0 -0
- package/templates/skills/id3-info-audit/SKILL.md +191 -0
- package/templates/skills/id3-preview/.gitkeep +0 -0
- package/templates/skills/id3-preview/SKILL.md +168 -0
- package/templates/skills/id3-spawn-team/.gitkeep +0 -0
- package/templates/skills/id3-spawn-team/SKILL.md +213 -0
- package/templates/specs/.gitkeep +0 -0
- package/templates/specs/data-model.md +26 -0
- package/templates/specs/entity-catalog.md +22 -0
- package/templates/specs/ui-design-contract.md +54 -0
- package/templates/specs/ui-inventory.md +24 -0
- package/templates/specs/ui-structure.md +32 -0
- package/templates/src/.gitkeep +0 -0
- package/templates/steering/.gitkeep +0 -0
- package/templates/steering/data-conventions.md +42 -0
- package/templates/steering/product.md +38 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/hooks/pre-commit-entry.ts
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
// src/hooks/shared.ts
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { execSync } from "node:child_process";
|
|
10
|
+
|
|
11
|
+
// src/utils/ascii.ts
|
|
12
|
+
function box(content, options = {}) {
|
|
13
|
+
const { title, width = 47, padding = 1 } = options;
|
|
14
|
+
const lines = content.split("\n");
|
|
15
|
+
const innerWidth = width - 2;
|
|
16
|
+
const padStr = " ".repeat(padding);
|
|
17
|
+
const result = [];
|
|
18
|
+
if (title) {
|
|
19
|
+
const titleStr = ` ${title} `;
|
|
20
|
+
const remaining = innerWidth - titleStr.length - 1;
|
|
21
|
+
result.push(` \u250C\u2500${titleStr}${"\u2500".repeat(Math.max(0, remaining))}\u2510`);
|
|
22
|
+
} else {
|
|
23
|
+
result.push(` \u250C${"\u2500".repeat(innerWidth)}\u2510`);
|
|
24
|
+
}
|
|
25
|
+
result.push(` \u2502${" ".repeat(innerWidth)}\u2502`);
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
const padded = `${padStr}${line}`;
|
|
28
|
+
const spaces = innerWidth - padded.length;
|
|
29
|
+
result.push(` \u2502${padded}${" ".repeat(Math.max(0, spaces))}\u2502`);
|
|
30
|
+
}
|
|
31
|
+
result.push(` \u2502${" ".repeat(innerWidth)}\u2502`);
|
|
32
|
+
result.push(` \u2514${"\u2500".repeat(innerWidth)}\u2518`);
|
|
33
|
+
return result.join("\n");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/hooks/shared.ts
|
|
37
|
+
function parseGitDiff(output) {
|
|
38
|
+
if (!output.trim()) return [];
|
|
39
|
+
return output.trim().split("\n").map((line) => {
|
|
40
|
+
const [status, path] = line.split(" ");
|
|
41
|
+
return { status, path };
|
|
42
|
+
}).filter((e) => e.path);
|
|
43
|
+
}
|
|
44
|
+
function getCachedDiff() {
|
|
45
|
+
try {
|
|
46
|
+
const output = execSync("git diff --cached --name-status", {
|
|
47
|
+
encoding: "utf-8"
|
|
48
|
+
});
|
|
49
|
+
return parseGitDiff(output);
|
|
50
|
+
} catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function getCachedFileContent(filePath) {
|
|
55
|
+
try {
|
|
56
|
+
return execSync(`git show :${filePath}`, { encoding: "utf-8" });
|
|
57
|
+
} catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function isSchemaFile(filePath, patterns) {
|
|
62
|
+
return patterns.some((pattern) => matchGlob(filePath, pattern));
|
|
63
|
+
}
|
|
64
|
+
function isValidationFile(filePath, patterns) {
|
|
65
|
+
return patterns.some((pattern) => matchGlob(filePath, pattern));
|
|
66
|
+
}
|
|
67
|
+
function matchGlob(filePath, pattern) {
|
|
68
|
+
const regex = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
|
|
69
|
+
return new RegExp(`^${regex}$`).test(filePath) || new RegExp(`(^|/)${regex}$`).test(filePath);
|
|
70
|
+
}
|
|
71
|
+
async function loadHookConfig(projectRoot2) {
|
|
72
|
+
try {
|
|
73
|
+
const configPath = join(
|
|
74
|
+
projectRoot2,
|
|
75
|
+
".claude",
|
|
76
|
+
"hooks",
|
|
77
|
+
"hook-config.json"
|
|
78
|
+
);
|
|
79
|
+
const content = await readFile(configPath, "utf-8");
|
|
80
|
+
return JSON.parse(content);
|
|
81
|
+
} catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function isSkipHooks() {
|
|
86
|
+
return process.env["IDDD_SKIP_HOOKS"] === "1";
|
|
87
|
+
}
|
|
88
|
+
async function logSkip(projectRoot2) {
|
|
89
|
+
const { appendFile, mkdir } = await import("node:fs/promises");
|
|
90
|
+
const logDir = join(projectRoot2, ".iddd");
|
|
91
|
+
await mkdir(logDir, { recursive: true });
|
|
92
|
+
const logPath = join(logDir, "skip-history.log");
|
|
93
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
94
|
+
let commitHash = "unknown";
|
|
95
|
+
try {
|
|
96
|
+
commitHash = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
|
|
97
|
+
} catch {
|
|
98
|
+
}
|
|
99
|
+
await appendFile(logPath, `${timestamp} ${commitHash} Hook skipped
|
|
100
|
+
`);
|
|
101
|
+
}
|
|
102
|
+
function printBlock(title, message) {
|
|
103
|
+
console.error(box(message, { title }));
|
|
104
|
+
}
|
|
105
|
+
function printWarn(title, message) {
|
|
106
|
+
console.error(box(message, { title }));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/hooks/schema-drift.ts
|
|
110
|
+
function analyzeSchemaChanges(diffContent, filePath) {
|
|
111
|
+
const changes = [];
|
|
112
|
+
const addedLines = diffContent.split("\n").filter((l) => l.startsWith("+") && !l.startsWith("+++")).map((l) => l.slice(1).trim());
|
|
113
|
+
for (const line of addedLines) {
|
|
114
|
+
const createMatch = line.match(/CREATE\s+TABLE\s+(\w+)/i);
|
|
115
|
+
if (createMatch) {
|
|
116
|
+
changes.push({
|
|
117
|
+
type: "add",
|
|
118
|
+
table: createMatch[1],
|
|
119
|
+
trivial: false,
|
|
120
|
+
description: `Table ${createMatch[1]} created`
|
|
121
|
+
});
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const alterAddMatch = line.match(
|
|
125
|
+
/ALTER\s+TABLE\s+(\w+)\s+ADD\s+(?:COLUMN\s+)?(\w+)/i
|
|
126
|
+
);
|
|
127
|
+
if (alterAddMatch) {
|
|
128
|
+
changes.push({
|
|
129
|
+
type: "add",
|
|
130
|
+
table: alterAddMatch[1],
|
|
131
|
+
column: alterAddMatch[2],
|
|
132
|
+
trivial: false,
|
|
133
|
+
description: `Column ${alterAddMatch[1]}.${alterAddMatch[2]} added`
|
|
134
|
+
});
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const dropMatch = line.match(/DROP\s+TABLE\s+(\w+)/i);
|
|
138
|
+
if (dropMatch) {
|
|
139
|
+
changes.push({
|
|
140
|
+
type: "drop",
|
|
141
|
+
table: dropMatch[1],
|
|
142
|
+
trivial: false,
|
|
143
|
+
description: `Table ${dropMatch[1]} dropped`
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const indexMatch = line.match(/CREATE\s+(?:UNIQUE\s+)?INDEX\s+/i);
|
|
148
|
+
if (indexMatch) {
|
|
149
|
+
const tableMatch = line.match(/ON\s+(\w+)/i);
|
|
150
|
+
changes.push({
|
|
151
|
+
type: "modify",
|
|
152
|
+
table: tableMatch?.[1] ?? "unknown",
|
|
153
|
+
trivial: true,
|
|
154
|
+
description: `Index added`
|
|
155
|
+
});
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const prismaModel = line.match(/^model\s+(\w+)\s*\{/);
|
|
159
|
+
if (prismaModel) {
|
|
160
|
+
changes.push({
|
|
161
|
+
type: "add",
|
|
162
|
+
table: prismaModel[1],
|
|
163
|
+
trivial: false,
|
|
164
|
+
description: `Model ${prismaModel[1]} added`
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
const djangoModel = line.match(/^class\s+(\w+)\(.*Model.*\)/);
|
|
169
|
+
if (djangoModel) {
|
|
170
|
+
changes.push({
|
|
171
|
+
type: "add",
|
|
172
|
+
table: djangoModel[1],
|
|
173
|
+
trivial: false,
|
|
174
|
+
description: `Model ${djangoModel[1]} added`
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return changes;
|
|
180
|
+
}
|
|
181
|
+
async function runSchemaDrift(projectRoot2) {
|
|
182
|
+
if (isSkipHooks()) {
|
|
183
|
+
await logSkip(projectRoot2);
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
const config = await loadHookConfig(projectRoot2);
|
|
187
|
+
if (!config?.hooks["pre-commit"]["schema-drift"]?.enabled) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
const { monitored_patterns } = config.hooks["pre-commit"]["schema-drift"];
|
|
191
|
+
const diff = getCachedDiff();
|
|
192
|
+
const schemaFiles = diff.filter(
|
|
193
|
+
(e) => isSchemaFile(e.path, monitored_patterns)
|
|
194
|
+
);
|
|
195
|
+
if (schemaFiles.length === 0) return true;
|
|
196
|
+
const catalogModified = diff.some(
|
|
197
|
+
(e) => e.path === "specs/entity-catalog.md"
|
|
198
|
+
);
|
|
199
|
+
const allChanges = [];
|
|
200
|
+
for (const file of schemaFiles) {
|
|
201
|
+
const content = getCachedFileContent(file.path);
|
|
202
|
+
if (content) {
|
|
203
|
+
allChanges.push(...analyzeSchemaChanges(content, file.path));
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (allChanges.length === 0) return true;
|
|
207
|
+
const hasStructural = allChanges.some((c) => !c.trivial);
|
|
208
|
+
if (catalogModified) {
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
if (!hasStructural) {
|
|
212
|
+
printWarn(
|
|
213
|
+
"\u26A0\uFE0F Schema Change Detected",
|
|
214
|
+
allChanges.map((c) => c.description).join("\n") + "\n\nConsider updating specs/entity-catalog.md"
|
|
215
|
+
);
|
|
216
|
+
return true;
|
|
217
|
+
}
|
|
218
|
+
const changeList = allChanges.filter((c) => !c.trivial).map((c) => ` ${c.description}`).join("\n");
|
|
219
|
+
printBlock(
|
|
220
|
+
"\u274C Schema Drift Detected",
|
|
221
|
+
`entity-catalog.md must be updated first.
|
|
222
|
+
|
|
223
|
+
${changeList}
|
|
224
|
+
|
|
225
|
+
Run /id3-info-audit or manually update
|
|
226
|
+
specs/entity-catalog.md`
|
|
227
|
+
);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/hooks/rule-check.ts
|
|
232
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
233
|
+
import { join as join2 } from "node:path";
|
|
234
|
+
var VALIDATION_PATTERNS = [
|
|
235
|
+
{
|
|
236
|
+
regex: /z\.\s*(?:object|string|number|boolean|array|enum)\s*\(/g,
|
|
237
|
+
label: "zod"
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
regex: /yup\.\s*(?:object|string|number|boolean|array)\s*\(/g,
|
|
241
|
+
label: "yup"
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
regex: /Joi\.\s*(?:object|string|number|boolean|array)\s*\(/g,
|
|
245
|
+
label: "joi"
|
|
246
|
+
},
|
|
247
|
+
{ regex: /CHECK\s*\(/gi, label: "SQL CHECK" },
|
|
248
|
+
{ regex: /NOT\s+NULL/gi, label: "SQL NOT NULL" },
|
|
249
|
+
{ regex: /ADD\s+.*UNIQUE/gi, label: "SQL UNIQUE" },
|
|
250
|
+
{ regex: /@validator\s*\(/g, label: "pydantic" },
|
|
251
|
+
{ regex: /@field_validator\s*\(/g, label: "pydantic-v2" },
|
|
252
|
+
{ regex: /@Valid/g, label: "java-valid" },
|
|
253
|
+
{ regex: /@NotNull/g, label: "java-notnull" },
|
|
254
|
+
{
|
|
255
|
+
regex: /@Column\s*\(\s*.*nullable\s*[:=]\s*false/g,
|
|
256
|
+
label: "orm-notnull"
|
|
257
|
+
},
|
|
258
|
+
{ regex: /@IsNotEmpty\s*\(/g, label: "class-validator" },
|
|
259
|
+
{
|
|
260
|
+
regex: /body\s*\(\s*['"].*['"]\s*\)\s*\.(?:not|is)/g,
|
|
261
|
+
label: "express-validator"
|
|
262
|
+
}
|
|
263
|
+
];
|
|
264
|
+
function detectValidationPatterns(content, filePath) {
|
|
265
|
+
const detections = [];
|
|
266
|
+
const lines = content.split("\n");
|
|
267
|
+
for (let i = 0; i < lines.length; i++) {
|
|
268
|
+
const line = lines[i];
|
|
269
|
+
if (!line.startsWith("+") && content.includes("\n+")) {
|
|
270
|
+
continue;
|
|
271
|
+
}
|
|
272
|
+
const cleanLine = line.replace(/^\+/, "");
|
|
273
|
+
for (const { regex, label } of VALIDATION_PATTERNS) {
|
|
274
|
+
regex.lastIndex = 0;
|
|
275
|
+
if (regex.test(cleanLine)) {
|
|
276
|
+
detections.push({
|
|
277
|
+
file: filePath,
|
|
278
|
+
line: i + 1,
|
|
279
|
+
content: cleanLine.trim(),
|
|
280
|
+
pattern: label
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return detections;
|
|
286
|
+
}
|
|
287
|
+
async function hasMatchingRule(projectRoot2, detection) {
|
|
288
|
+
try {
|
|
289
|
+
const rulesPath = join2(projectRoot2, "docs", "business-rules.md");
|
|
290
|
+
const rules = await readFile2(rulesPath, "utf-8");
|
|
291
|
+
return rules.includes("BR-") && rules.length > 200;
|
|
292
|
+
} catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async function runRuleCheck(projectRoot2) {
|
|
297
|
+
if (isSkipHooks()) {
|
|
298
|
+
await logSkip(projectRoot2);
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
301
|
+
const config = await loadHookConfig(projectRoot2);
|
|
302
|
+
if (!config?.hooks["pre-commit"]["rule-check"]?.enabled) {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
const { validation_patterns } = config.hooks["pre-commit"]["rule-check"];
|
|
306
|
+
const diff = getCachedDiff();
|
|
307
|
+
const validationFiles = diff.filter(
|
|
308
|
+
(e) => isValidationFile(e.path, validation_patterns)
|
|
309
|
+
);
|
|
310
|
+
if (validationFiles.length === 0) return true;
|
|
311
|
+
const allDetections = [];
|
|
312
|
+
for (const file of validationFiles) {
|
|
313
|
+
const content = getCachedFileContent(file.path);
|
|
314
|
+
if (content) {
|
|
315
|
+
allDetections.push(...detectValidationPatterns(content, file.path));
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (allDetections.length === 0) return true;
|
|
319
|
+
for (const detection of allDetections) {
|
|
320
|
+
const matched = await hasMatchingRule(projectRoot2, detection);
|
|
321
|
+
if (!matched) {
|
|
322
|
+
printWarn(
|
|
323
|
+
"\u26A0\uFE0F New Validation Detected",
|
|
324
|
+
`File: ${detection.file}:${detection.line}
|
|
325
|
+
Pattern: ${detection.pattern}
|
|
326
|
+
Content: ${detection.content}
|
|
327
|
+
|
|
328
|
+
No matching BR-xxx in business-rules.md
|
|
329
|
+
Consider registering this rule.`
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return true;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// src/hooks/pre-commit-entry.ts
|
|
337
|
+
var projectRoot = resolve(".");
|
|
338
|
+
async function main() {
|
|
339
|
+
const schemaDriftOk = await runSchemaDrift(projectRoot);
|
|
340
|
+
if (!schemaDriftOk) {
|
|
341
|
+
process.exit(1);
|
|
342
|
+
}
|
|
343
|
+
await runRuleCheck(projectRoot);
|
|
344
|
+
}
|
|
345
|
+
main().catch((err) => {
|
|
346
|
+
console.error("IDDD pre-commit hook error:", err);
|
|
347
|
+
process.exit(1);
|
|
348
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "id3-cli",
|
|
3
|
+
"version": "0.9.1",
|
|
4
|
+
"description": "Information Design-Driven Development — installable skill package for AI coding agents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"id3-cli": "dist/bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/",
|
|
11
|
+
"templates/"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc && node dist/scripts/build-hooks.js",
|
|
15
|
+
"build:cli": "tsc",
|
|
16
|
+
"build:hooks": "node dist/scripts/build-hooks.js",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"test:watch": "vitest",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"author": "Bruce Jung <sunghunet@gmail.com>",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"keywords": [
|
|
24
|
+
"iddd",
|
|
25
|
+
"claude-code",
|
|
26
|
+
"codex",
|
|
27
|
+
"information-design",
|
|
28
|
+
"data-modeling",
|
|
29
|
+
"agent-skills"
|
|
30
|
+
],
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^25.5.2",
|
|
33
|
+
"esbuild": "^0.25.0",
|
|
34
|
+
"typescript": "^5.8.0",
|
|
35
|
+
"vitest": "^3.1.0"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"enabled": true,
|
|
3
|
+
"hooks": {
|
|
4
|
+
"pre-commit": {
|
|
5
|
+
"schema-drift": {
|
|
6
|
+
"enabled": true,
|
|
7
|
+
"severity": "block",
|
|
8
|
+
"monitored_patterns": [
|
|
9
|
+
"prisma/schema.prisma",
|
|
10
|
+
"drizzle/**/*.ts",
|
|
11
|
+
"**/migrations/*.sql",
|
|
12
|
+
"**/models.py",
|
|
13
|
+
"**/entities/*.ts",
|
|
14
|
+
"**/entities/*.java"
|
|
15
|
+
]
|
|
16
|
+
},
|
|
17
|
+
"rule-check": {
|
|
18
|
+
"enabled": true,
|
|
19
|
+
"severity": "warn",
|
|
20
|
+
"validation_patterns": [
|
|
21
|
+
"*.schema.ts",
|
|
22
|
+
"*.validator.*",
|
|
23
|
+
"**/validators/**"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"post-commit": {
|
|
28
|
+
"auto-audit": {
|
|
29
|
+
"enabled": true,
|
|
30
|
+
"interval_commits": 10
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "Bash",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node hooks/iddd-schema-drift.js",
|
|
10
|
+
"statusMessage": "Checking IDDD schema drift"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PostToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Bash",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "node hooks/iddd-rule-check.js",
|
|
22
|
+
"statusMessage": "Checking IDDD business rules"
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"SessionStart": [
|
|
28
|
+
{
|
|
29
|
+
"matcher": "startup|resume",
|
|
30
|
+
"hooks": [
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "node hooks/iddd-auto-audit.js",
|
|
34
|
+
"statusMessage": "IDDD entropy check"
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0
|
|
File without changes
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# IDDD - Information Design-Driven Development
|
|
2
|
+
|
|
3
|
+
You are working in a project that follows **IDDD (Information Design-Driven Development)**. The information model is the generative center of all development. Every artifact - requirements, API contracts, screen designs, business rules, tests - is derived from and traceable to `specs/entity-catalog.md`.
|
|
4
|
+
|
|
5
|
+
**Core principle:** Start from "what information exists?" rather than "what features should we build?"
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Principles
|
|
10
|
+
|
|
11
|
+
1. **Information model is the single source of truth.** All code, APIs, UI, and tests are derived from entity-catalog.md and data-model.md. If code disagrees with the spec, the spec wins - update the code, not the spec (unless there is a deliberate model change).
|
|
12
|
+
|
|
13
|
+
2. **Entity-first identification.** Before writing any code, entities must be identified and documented. New features start with "what entities are involved?" not "what endpoints do we need?"
|
|
14
|
+
|
|
15
|
+
3. **Data model traceability.** Every column, constraint, and relationship in the codebase must trace back to an entry in entity-catalog.md and data-model.md. Untracked schema elements are considered drift.
|
|
16
|
+
|
|
17
|
+
4. **Output-first design.** Design what users see (dashboards, reports, lists) before designing inputs (forms, APIs). The output image drives the information model.
|
|
18
|
+
|
|
19
|
+
5. **Business rules are explicit.** Every validation, constraint, and derivation rule is registered in `docs/business-rules.md` with a BR-xxx identifier. Code-only rules are considered debt.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Required Reference Files
|
|
24
|
+
|
|
25
|
+
Before starting any task, read these files to understand the current state:
|
|
26
|
+
|
|
27
|
+
| File | Purpose |
|
|
28
|
+
|------|---------|
|
|
29
|
+
| `specs/entity-catalog.md` | Entity definitions, attributes, relationships, business rule references |
|
|
30
|
+
| `specs/data-model.md` | Mermaid ERD, design decisions, index strategy |
|
|
31
|
+
| `specs/ui-structure.md` | Screen inventory and navigation structure (Phase 2.5) |
|
|
32
|
+
| `specs/ui-design-contract.md` | Visual design contract -- tokens, components, copywriting (Phase 2.5) |
|
|
33
|
+
| `specs/ui-inventory.md` | Current UI structure (brownfield projects) |
|
|
34
|
+
| `docs/business-rules.md` | All business rules (BR-xxx) with enforcement locations |
|
|
35
|
+
| `steering/data-conventions.md` | Naming, typing, PK strategy, and structural conventions |
|
|
36
|
+
| `steering/product.md` | Product vision, scope, and priorities |
|
|
37
|
+
| `docs/domain-glossary.md` | Domain terms and definitions |
|
|
38
|
+
| `docs/info-debt.md` | Known discrepancies and technical debt items |
|
|
39
|
+
| `docs/model-changelog.md` | History of model changes (Keep a Changelog format) |
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## New Feature Workflow
|
|
44
|
+
|
|
45
|
+
When adding a new feature, follow this procedure:
|
|
46
|
+
|
|
47
|
+
1. **Identify entities involved.** Read `specs/entity-catalog.md`. If new entities are needed, run entity identification first.
|
|
48
|
+
2. **Check business rules.** Read `docs/business-rules.md` for relevant BR-xxx rules that govern the feature's behavior.
|
|
49
|
+
3. **Check UI structure.** Read `specs/ui-structure.md` for screen inventory and `specs/ui-design-contract.md` for design tokens.
|
|
50
|
+
4. **Implement following the spec.** Every line of code should trace to a spec entry (entity attribute, relationship, or BR-xxx rule).
|
|
51
|
+
5. **Register new rules.** If new validation logic is added, register it as BR-xxx in `docs/business-rules.md`.
|
|
52
|
+
6. **Update the changelog.** Record what changed in `docs/model-changelog.md`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Security Rules
|
|
57
|
+
|
|
58
|
+
1. **Never commit secrets.** Do not add `.env`, credentials, API keys, or tokens to version control.
|
|
59
|
+
2. **Never skip hooks without logging.** Hook bypasses must be recorded and resolved.
|
|
60
|
+
3. **Never modify spec files during implementation.** If implementation reveals a spec gap, escalate - do not silently change the spec.
|
|
61
|
+
4. **Never delete entities from entity-catalog.md without a changelog entry.** Removals must be documented.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Harness Constraints
|
|
66
|
+
|
|
67
|
+
The project uses automated hooks that enforce information model consistency:
|
|
68
|
+
|
|
69
|
+
### Schema Drift Detection (severity: BLOCK)
|
|
70
|
+
|
|
71
|
+
When modifying schema-related files (migrations, ORM definitions, model files), the harness checks whether `specs/entity-catalog.md` has been updated accordingly. If not, the change is **blocked**.
|
|
72
|
+
|
|
73
|
+
**Required workflow:** Update `specs/entity-catalog.md` first, then modify the schema.
|
|
74
|
+
|
|
75
|
+
### Rule Check (severity: WARN)
|
|
76
|
+
|
|
77
|
+
When adding validation logic (Zod, Yup, Joi, Pydantic, @Valid, etc.), the harness checks for a corresponding BR-xxx entry in `docs/business-rules.md`. If missing, you receive a warning.
|
|
78
|
+
|
|
79
|
+
**Required workflow:** Register the business rule in `docs/business-rules.md` before or alongside the validation code.
|
|
80
|
+
|
|
81
|
+
### Auto-Audit (severity: INFO)
|
|
82
|
+
|
|
83
|
+
After every N commits (configurable), the harness runs an automatic audit comparing the codebase against the information model. Review findings at the start of the next session.
|
|
84
|
+
|
|
85
|
+
### Hook Bypass
|
|
86
|
+
|
|
87
|
+
- Set `IDDD_SKIP_HOOKS=1` to skip all hooks temporarily.
|
|
88
|
+
- Bypasses are logged to `.iddd/skip-history.log` and reviewed during audits.
|
|
89
|
+
- Avoid bypassing hooks unless absolutely necessary.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Entropy Management
|
|
94
|
+
|
|
95
|
+
### Version Headers
|
|
96
|
+
|
|
97
|
+
`specs/entity-catalog.md` and `specs/data-model.md` contain YAML frontmatter:
|
|
98
|
+
|
|
99
|
+
```yaml
|
|
100
|
+
---
|
|
101
|
+
version: "1.0"
|
|
102
|
+
last_verified: "YYYY-MM-DD"
|
|
103
|
+
phase: "Phase 2 Complete"
|
|
104
|
+
entity_count: N
|
|
105
|
+
rule_count: N
|
|
106
|
+
audit_status: "clean"
|
|
107
|
+
---
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- Update `last_verified` whenever the model is audited or verified.
|
|
111
|
+
- If `last_verified` is more than 7 days old, run an audit before proceeding with new work.
|
|
112
|
+
|
|
113
|
+
### Change Log
|
|
114
|
+
|
|
115
|
+
Record all model changes in `docs/model-changelog.md` using Keep a Changelog format.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## Multi-Agent Workflow
|
|
120
|
+
|
|
121
|
+
When multiple agents collaborate on this project, use the following role assignments and coordination rules. This workflow applies to both Codex multi-agent orchestration and any platform that supports parallel agent execution.
|
|
122
|
+
|
|
123
|
+
### Agent Roles
|
|
124
|
+
|
|
125
|
+
#### spec-generator
|
|
126
|
+
|
|
127
|
+
**Responsibility:** Transform the information model into implementation-ready specifications.
|
|
128
|
+
|
|
129
|
+
**Input files:**
|
|
130
|
+
- `specs/entity-catalog.md`
|
|
131
|
+
- `specs/data-model.md`
|
|
132
|
+
- `specs/ui-structure.md`
|
|
133
|
+
- `specs/ui-design-contract.md`
|
|
134
|
+
- `specs/ui-inventory.md`
|
|
135
|
+
- `docs/business-rules.md`
|
|
136
|
+
- `steering/data-conventions.md`
|
|
137
|
+
|
|
138
|
+
**Output:** Per-entity or per-feature-group `requirements.md` and `api-contracts.md`.
|
|
139
|
+
|
|
140
|
+
**Rules:**
|
|
141
|
+
- Every requirement must reference an entity-catalog.md entry or BR-xxx rule.
|
|
142
|
+
- Do not invent requirements not grounded in the information model.
|
|
143
|
+
- Follow `steering/data-conventions.md` for naming and typing.
|
|
144
|
+
- Flag ambiguities rather than assuming.
|
|
145
|
+
|
|
146
|
+
#### implementer
|
|
147
|
+
|
|
148
|
+
**Responsibility:** Implement code based on specs from spec-generator.
|
|
149
|
+
|
|
150
|
+
**Input files:** All spec-generator inputs plus spec-generator output.
|
|
151
|
+
|
|
152
|
+
**Rules:**
|
|
153
|
+
- Commit per entity (one entity per commit with a descriptive message).
|
|
154
|
+
- Follow the project's tech stack and conventions.
|
|
155
|
+
- Enforce every BR-xxx rule at the specified location (DB constraint, application logic, or both).
|
|
156
|
+
- Never modify `specs/` or `docs/` files directly. If a spec gap is discovered, notify spec-generator and qa-reviewer.
|
|
157
|
+
- Each commit is atomic: model + migration + API + validation + tests for one entity.
|
|
158
|
+
|
|
159
|
+
#### qa-reviewer
|
|
160
|
+
|
|
161
|
+
**Responsibility:** Verify implementation against the information model.
|
|
162
|
+
|
|
163
|
+
**Input files:**
|
|
164
|
+
- `specs/entity-catalog.md`
|
|
165
|
+
- `specs/ui-structure.md`
|
|
166
|
+
- `specs/ui-design-contract.md`
|
|
167
|
+
- `docs/business-rules.md`
|
|
168
|
+
|
|
169
|
+
**Rules:**
|
|
170
|
+
- Check every entity implementation against entity-catalog.md (attributes, types, constraints).
|
|
171
|
+
- Verify all BR-xxx rules are enforced at the correct location.
|
|
172
|
+
- Verify relationships match the ERD (FK placement, delete rules, cardinality).
|
|
173
|
+
- Every finding must cite the specific spec entry that was violated.
|
|
174
|
+
- Approve only when implementation matches the information model.
|
|
175
|
+
|
|
176
|
+
### Task Generation
|
|
177
|
+
|
|
178
|
+
1. Read `specs/entity-catalog.md` to extract the entity list.
|
|
179
|
+
2. Create one task per entity.
|
|
180
|
+
3. Analyze FK dependencies from `specs/data-model.md`.
|
|
181
|
+
4. Independent entities run in parallel; dependent entities wait for parents.
|
|
182
|
+
5. Within each wave, assign tasks across agents.
|
|
183
|
+
|
|
184
|
+
### Coordination Protocol
|
|
185
|
+
|
|
186
|
+
- spec-generator completes specs before implementer begins work on an entity.
|
|
187
|
+
- implementer completes implementation before qa-reviewer begins review.
|
|
188
|
+
- qa-reviewer rejects with specific findings referencing spec entries; implementer fixes and resubmits.
|
|
189
|
+
- When all entities pass review, the phase is complete.
|
|
190
|
+
|
|
191
|
+
---
|
|
192
|
+
|
|
193
|
+
## IDDD Skills
|
|
194
|
+
|
|
195
|
+
The following skills are available in the `skills/` directory:
|
|
196
|
+
|
|
197
|
+
| Skill | Phase | Purpose |
|
|
198
|
+
|-------|-------|---------|
|
|
199
|
+
| id3-identify-entities | 0/1 | Discover entities from existing code or structured interview |
|
|
200
|
+
| id3-design-information | 2 | Refine conceptual model into logical model with rules and UI proposals |
|
|
201
|
+
| id3-design-ui | 2.5 | UI structure derivation, visual design contract, mockup generation |
|
|
202
|
+
| id3-spawn-team | 3-5 | Spawn parallel agents for spec generation, implementation, and QA |
|
|
203
|
+
| id3-info-audit | Audit | Comprehensive information model audit against codebase |
|
|
204
|
+
| id3-preview | Utility | Start preview server for ERD, UI mockups, and audit reports |
|