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.
Files changed (114) hide show
  1. package/README.ja-JP.md +769 -0
  2. package/README.ko-KR.md +769 -0
  3. package/README.md +769 -0
  4. package/README.tr-TR.md +769 -0
  5. package/README.zh-CN.md +769 -0
  6. package/dist/bin/cli.d.ts +2 -0
  7. package/dist/bin/cli.js +40 -0
  8. package/dist/bin/cli.js.map +1 -0
  9. package/dist/scripts/build-hooks.d.ts +1 -0
  10. package/dist/scripts/build-hooks.js +58 -0
  11. package/dist/scripts/build-hooks.js.map +1 -0
  12. package/dist/src/hooks/auto-audit.d.ts +4 -0
  13. package/dist/src/hooks/auto-audit.js +47 -0
  14. package/dist/src/hooks/auto-audit.js.map +1 -0
  15. package/dist/src/hooks/claude-pretool-entry.d.ts +1 -0
  16. package/dist/src/hooks/claude-pretool-entry.js +36 -0
  17. package/dist/src/hooks/claude-pretool-entry.js.map +1 -0
  18. package/dist/src/hooks/claude-stop-entry.d.ts +1 -0
  19. package/dist/src/hooks/claude-stop-entry.js +7 -0
  20. package/dist/src/hooks/claude-stop-entry.js.map +1 -0
  21. package/dist/src/hooks/post-commit-entry.d.ts +1 -0
  22. package/dist/src/hooks/post-commit-entry.js +7 -0
  23. package/dist/src/hooks/post-commit-entry.js.map +1 -0
  24. package/dist/src/hooks/pre-commit-entry.d.ts +1 -0
  25. package/dist/src/hooks/pre-commit-entry.js +16 -0
  26. package/dist/src/hooks/pre-commit-entry.js.map +1 -0
  27. package/dist/src/hooks/rule-check.d.ts +8 -0
  28. package/dist/src/hooks/rule-check.js +101 -0
  29. package/dist/src/hooks/rule-check.js.map +1 -0
  30. package/dist/src/hooks/schema-drift.d.ts +17 -0
  31. package/dist/src/hooks/schema-drift.js +151 -0
  32. package/dist/src/hooks/schema-drift.js.map +1 -0
  33. package/dist/src/hooks/shared.d.ts +43 -0
  34. package/dist/src/hooks/shared.js +98 -0
  35. package/dist/src/hooks/shared.js.map +1 -0
  36. package/dist/src/init.d.ts +20 -0
  37. package/dist/src/init.js +193 -0
  38. package/dist/src/init.js.map +1 -0
  39. package/dist/src/preview/mockup-generator.d.ts +56 -0
  40. package/dist/src/preview/mockup-generator.js +402 -0
  41. package/dist/src/preview/mockup-generator.js.map +1 -0
  42. package/dist/src/preview/renderer.d.ts +30 -0
  43. package/dist/src/preview/renderer.js +145 -0
  44. package/dist/src/preview/renderer.js.map +1 -0
  45. package/dist/src/preview/server.d.ts +9 -0
  46. package/dist/src/preview/server.js +55 -0
  47. package/dist/src/preview/server.js.map +1 -0
  48. package/dist/src/preview/ui-auditor.d.ts +27 -0
  49. package/dist/src/preview/ui-auditor.js +141 -0
  50. package/dist/src/preview/ui-auditor.js.map +1 -0
  51. package/dist/src/preview/ui-gate.d.ts +66 -0
  52. package/dist/src/preview/ui-gate.js +210 -0
  53. package/dist/src/preview/ui-gate.js.map +1 -0
  54. package/dist/src/utils/ascii.d.ts +7 -0
  55. package/dist/src/utils/ascii.js +41 -0
  56. package/dist/src/utils/ascii.js.map +1 -0
  57. package/dist/src/utils/fs.d.ts +6 -0
  58. package/dist/src/utils/fs.js +39 -0
  59. package/dist/src/utils/fs.js.map +1 -0
  60. package/dist/templates/hooks/iddd-auto-audit.js +121 -0
  61. package/dist/templates/hooks/iddd-schema-drift.js +279 -0
  62. package/dist/templates/hooks/post-commit +121 -0
  63. package/dist/templates/hooks/pre-commit +348 -0
  64. package/package.json +37 -0
  65. package/templates/.agents/skills/.gitkeep +0 -0
  66. package/templates/.claude/hooks/.gitkeep +0 -0
  67. package/templates/.claude/hooks/hook-config.json +34 -0
  68. package/templates/.claude/skills/.gitkeep +0 -0
  69. package/templates/.codex/.gitkeep +0 -0
  70. package/templates/.codex/hooks.json +40 -0
  71. package/templates/.iddd/commit-count +1 -0
  72. package/templates/.iddd/preview/.gitkeep +0 -0
  73. package/templates/AGENTS.md +204 -0
  74. package/templates/CLAUDE.md +215 -0
  75. package/templates/README.md +476 -0
  76. package/templates/docs/.gitkeep +0 -0
  77. package/templates/docs/business-rules.md +14 -0
  78. package/templates/docs/domain-glossary.md +8 -0
  79. package/templates/docs/info-debt.md +17 -0
  80. package/templates/docs/model-changelog.md +12 -0
  81. package/templates/hooks/.gitkeep +0 -0
  82. package/templates/hooks/iddd-auto-audit.js +121 -0
  83. package/templates/hooks/iddd-schema-drift.js +279 -0
  84. package/templates/hooks/post-commit +121 -0
  85. package/templates/hooks/pre-commit +348 -0
  86. package/templates/skills/id3-design-information/SKILL.md +170 -0
  87. package/templates/skills/id3-design-information/references/phase2-procedure.md +241 -0
  88. package/templates/skills/id3-design-ui/.gitkeep +0 -0
  89. package/templates/skills/id3-design-ui/SKILL.md +200 -0
  90. package/templates/skills/id3-design-ui/references/.gitkeep +0 -0
  91. package/templates/skills/id3-design-ui/references/step1-structure-derivation.md +177 -0
  92. package/templates/skills/id3-design-ui/references/step2-visual-contract.md +257 -0
  93. package/templates/skills/id3-design-ui/references/step3-gate-and-mockup.md +177 -0
  94. package/templates/skills/id3-design-ui/references/step4-implementation.md +244 -0
  95. package/templates/skills/id3-identify-entities/SKILL.md +239 -0
  96. package/templates/skills/id3-identify-entities/references/.gitkeep +0 -0
  97. package/templates/skills/id3-identify-entities/references/phase0-brownfield.md +377 -0
  98. package/templates/skills/id3-identify-entities/references/phase1-greenfield.md +319 -0
  99. package/templates/skills/id3-info-audit/.gitkeep +0 -0
  100. package/templates/skills/id3-info-audit/SKILL.md +191 -0
  101. package/templates/skills/id3-preview/.gitkeep +0 -0
  102. package/templates/skills/id3-preview/SKILL.md +168 -0
  103. package/templates/skills/id3-spawn-team/.gitkeep +0 -0
  104. package/templates/skills/id3-spawn-team/SKILL.md +213 -0
  105. package/templates/specs/.gitkeep +0 -0
  106. package/templates/specs/data-model.md +26 -0
  107. package/templates/specs/entity-catalog.md +22 -0
  108. package/templates/specs/ui-design-contract.md +54 -0
  109. package/templates/specs/ui-inventory.md +24 -0
  110. package/templates/specs/ui-structure.md +32 -0
  111. package/templates/src/.gitkeep +0 -0
  112. package/templates/steering/.gitkeep +0 -0
  113. package/templates/steering/data-conventions.md +42 -0
  114. package/templates/steering/product.md +38 -0
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/claude-stop-entry.ts
4
+ import { resolve } from "node:path";
5
+
6
+ // src/hooks/auto-audit.ts
7
+ import { readFile as readFile2, writeFile, mkdir } from "node:fs/promises";
8
+ import { join as join2 } from "node:path";
9
+
10
+ // src/hooks/shared.ts
11
+ import { readFile } from "node:fs/promises";
12
+ import { join } from "node:path";
13
+ import { execSync } from "node:child_process";
14
+
15
+ // src/utils/ascii.ts
16
+ function box(content, options = {}) {
17
+ const { title, width = 47, padding = 1 } = options;
18
+ const lines = content.split("\n");
19
+ const innerWidth = width - 2;
20
+ const padStr = " ".repeat(padding);
21
+ const result = [];
22
+ if (title) {
23
+ const titleStr = ` ${title} `;
24
+ const remaining = innerWidth - titleStr.length - 1;
25
+ result.push(` \u250C\u2500${titleStr}${"\u2500".repeat(Math.max(0, remaining))}\u2510`);
26
+ } else {
27
+ result.push(` \u250C${"\u2500".repeat(innerWidth)}\u2510`);
28
+ }
29
+ result.push(` \u2502${" ".repeat(innerWidth)}\u2502`);
30
+ for (const line of lines) {
31
+ const padded = `${padStr}${line}`;
32
+ const spaces = innerWidth - padded.length;
33
+ result.push(` \u2502${padded}${" ".repeat(Math.max(0, spaces))}\u2502`);
34
+ }
35
+ result.push(` \u2502${" ".repeat(innerWidth)}\u2502`);
36
+ result.push(` \u2514${"\u2500".repeat(innerWidth)}\u2518`);
37
+ return result.join("\n");
38
+ }
39
+
40
+ // src/hooks/shared.ts
41
+ async function loadHookConfig(projectRoot2) {
42
+ try {
43
+ const configPath = join(
44
+ projectRoot2,
45
+ ".claude",
46
+ "hooks",
47
+ "hook-config.json"
48
+ );
49
+ const content = await readFile(configPath, "utf-8");
50
+ return JSON.parse(content);
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ function isSkipHooks() {
56
+ return process.env["IDDD_SKIP_HOOKS"] === "1";
57
+ }
58
+ async function logSkip(projectRoot2) {
59
+ const { appendFile, mkdir: mkdir2 } = await import("node:fs/promises");
60
+ const logDir = join(projectRoot2, ".iddd");
61
+ await mkdir2(logDir, { recursive: true });
62
+ const logPath = join(logDir, "skip-history.log");
63
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
64
+ let commitHash = "unknown";
65
+ try {
66
+ commitHash = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
67
+ } catch {
68
+ }
69
+ await appendFile(logPath, `${timestamp} ${commitHash} Hook skipped
70
+ `);
71
+ }
72
+ function printWarn(title, message) {
73
+ console.error(box(message, { title }));
74
+ }
75
+
76
+ // src/hooks/auto-audit.ts
77
+ async function incrementCounter(projectRoot2) {
78
+ const counterPath = join2(projectRoot2, ".iddd", "commit-count");
79
+ let count = 0;
80
+ try {
81
+ const content = await readFile2(counterPath, "utf-8");
82
+ count = parseInt(content.trim(), 10) || 0;
83
+ } catch {
84
+ }
85
+ count++;
86
+ await mkdir(join2(projectRoot2, ".iddd"), { recursive: true });
87
+ await writeFile(counterPath, count.toString());
88
+ return count;
89
+ }
90
+ async function resetCounter(projectRoot2) {
91
+ const counterPath = join2(projectRoot2, ".iddd", "commit-count");
92
+ await writeFile(counterPath, "0");
93
+ }
94
+ async function runAutoAudit(projectRoot2) {
95
+ if (isSkipHooks()) {
96
+ await logSkip(projectRoot2);
97
+ return;
98
+ }
99
+ const config = await loadHookConfig(projectRoot2);
100
+ if (!config?.hooks["post-commit"]["auto-audit"]?.enabled) {
101
+ return;
102
+ }
103
+ const threshold = config.hooks["post-commit"]["auto-audit"].interval_commits;
104
+ const count = await incrementCounter(projectRoot2);
105
+ if (count >= threshold) {
106
+ printWarn(
107
+ "\u2139\uFE0F Auto-Audit Triggered",
108
+ `${count} commits since last audit.
109
+ Run /id3-info-audit for a full check.
110
+
111
+ Report saved to .iddd/last-audit-report.md`
112
+ );
113
+ await resetCounter(projectRoot2);
114
+ }
115
+ }
116
+
117
+ // src/hooks/claude-stop-entry.ts
118
+ var projectRoot = resolve(".");
119
+ runAutoAudit(projectRoot).catch((err) => {
120
+ console.error("IDDD claude-stop hook error:", err);
121
+ });
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/claude-pretool-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 parseHookInput(stdinData) {
45
+ try {
46
+ const parsed = JSON.parse(stdinData);
47
+ if (!parsed.tool_name || !parsed.tool_input?.file_path) {
48
+ return null;
49
+ }
50
+ return {
51
+ toolName: parsed.tool_name,
52
+ filePath: parsed.tool_input.file_path
53
+ };
54
+ } catch {
55
+ return null;
56
+ }
57
+ }
58
+ function getCachedDiff() {
59
+ try {
60
+ const output = execSync("git diff --cached --name-status", {
61
+ encoding: "utf-8"
62
+ });
63
+ return parseGitDiff(output);
64
+ } catch {
65
+ return [];
66
+ }
67
+ }
68
+ function getCachedFileContent(filePath) {
69
+ try {
70
+ return execSync(`git show :${filePath}`, { encoding: "utf-8" });
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+ function isSchemaFile(filePath, patterns) {
76
+ return patterns.some((pattern) => matchGlob(filePath, pattern));
77
+ }
78
+ function isValidationFile(filePath, patterns) {
79
+ return patterns.some((pattern) => matchGlob(filePath, pattern));
80
+ }
81
+ function matchGlob(filePath, pattern) {
82
+ const regex = pattern.replace(/\./g, "\\.").replace(/\*\*/g, "{{GLOBSTAR}}").replace(/\*/g, "[^/]*").replace(/\{\{GLOBSTAR\}\}/g, ".*");
83
+ return new RegExp(`^${regex}$`).test(filePath) || new RegExp(`(^|/)${regex}$`).test(filePath);
84
+ }
85
+ async function loadHookConfig(projectRoot2) {
86
+ try {
87
+ const configPath = join(
88
+ projectRoot2,
89
+ ".claude",
90
+ "hooks",
91
+ "hook-config.json"
92
+ );
93
+ const content = await readFile(configPath, "utf-8");
94
+ return JSON.parse(content);
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+ function isSkipHooks() {
100
+ return process.env["IDDD_SKIP_HOOKS"] === "1";
101
+ }
102
+ async function logSkip(projectRoot2) {
103
+ const { appendFile, mkdir } = await import("node:fs/promises");
104
+ const logDir = join(projectRoot2, ".iddd");
105
+ await mkdir(logDir, { recursive: true });
106
+ const logPath = join(logDir, "skip-history.log");
107
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
108
+ let commitHash = "unknown";
109
+ try {
110
+ commitHash = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
111
+ } catch {
112
+ }
113
+ await appendFile(logPath, `${timestamp} ${commitHash} Hook skipped
114
+ `);
115
+ }
116
+ function printWarn(title, message) {
117
+ console.error(box(message, { title }));
118
+ }
119
+
120
+ // src/hooks/schema-drift.ts
121
+ async function runSchemaDriftFromHookInput(projectRoot2, hookInput) {
122
+ if (isSkipHooks()) {
123
+ await logSkip(projectRoot2);
124
+ return true;
125
+ }
126
+ const config = await loadHookConfig(projectRoot2);
127
+ if (!config?.hooks["pre-commit"]["schema-drift"]?.enabled) {
128
+ return true;
129
+ }
130
+ const { monitored_patterns } = config.hooks["pre-commit"]["schema-drift"];
131
+ const relativePath = hookInput.filePath.startsWith(projectRoot2) ? hookInput.filePath.slice(projectRoot2.length + 1) : hookInput.filePath;
132
+ if (!isSchemaFile(relativePath, monitored_patterns)) {
133
+ return true;
134
+ }
135
+ printWarn(
136
+ "\u26A0\uFE0F Schema File Modified",
137
+ `File: ${relativePath}
138
+
139
+ Ensure specs/entity-catalog.md is updated
140
+ to reflect this schema change.`
141
+ );
142
+ return true;
143
+ }
144
+
145
+ // src/hooks/rule-check.ts
146
+ import { readFile as readFile2 } from "node:fs/promises";
147
+ import { join as join2 } from "node:path";
148
+ var VALIDATION_PATTERNS = [
149
+ {
150
+ regex: /z\.\s*(?:object|string|number|boolean|array|enum)\s*\(/g,
151
+ label: "zod"
152
+ },
153
+ {
154
+ regex: /yup\.\s*(?:object|string|number|boolean|array)\s*\(/g,
155
+ label: "yup"
156
+ },
157
+ {
158
+ regex: /Joi\.\s*(?:object|string|number|boolean|array)\s*\(/g,
159
+ label: "joi"
160
+ },
161
+ { regex: /CHECK\s*\(/gi, label: "SQL CHECK" },
162
+ { regex: /NOT\s+NULL/gi, label: "SQL NOT NULL" },
163
+ { regex: /ADD\s+.*UNIQUE/gi, label: "SQL UNIQUE" },
164
+ { regex: /@validator\s*\(/g, label: "pydantic" },
165
+ { regex: /@field_validator\s*\(/g, label: "pydantic-v2" },
166
+ { regex: /@Valid/g, label: "java-valid" },
167
+ { regex: /@NotNull/g, label: "java-notnull" },
168
+ {
169
+ regex: /@Column\s*\(\s*.*nullable\s*[:=]\s*false/g,
170
+ label: "orm-notnull"
171
+ },
172
+ { regex: /@IsNotEmpty\s*\(/g, label: "class-validator" },
173
+ {
174
+ regex: /body\s*\(\s*['"].*['"]\s*\)\s*\.(?:not|is)/g,
175
+ label: "express-validator"
176
+ }
177
+ ];
178
+ function detectValidationPatterns(content, filePath) {
179
+ const detections = [];
180
+ const lines = content.split("\n");
181
+ for (let i = 0; i < lines.length; i++) {
182
+ const line = lines[i];
183
+ if (!line.startsWith("+") && content.includes("\n+")) {
184
+ continue;
185
+ }
186
+ const cleanLine = line.replace(/^\+/, "");
187
+ for (const { regex, label } of VALIDATION_PATTERNS) {
188
+ regex.lastIndex = 0;
189
+ if (regex.test(cleanLine)) {
190
+ detections.push({
191
+ file: filePath,
192
+ line: i + 1,
193
+ content: cleanLine.trim(),
194
+ pattern: label
195
+ });
196
+ }
197
+ }
198
+ }
199
+ return detections;
200
+ }
201
+ async function hasMatchingRule(projectRoot2, detection) {
202
+ try {
203
+ const rulesPath = join2(projectRoot2, "docs", "business-rules.md");
204
+ const rules = await readFile2(rulesPath, "utf-8");
205
+ return rules.includes("BR-") && rules.length > 200;
206
+ } catch {
207
+ return false;
208
+ }
209
+ }
210
+ async function runRuleCheck(projectRoot2) {
211
+ if (isSkipHooks()) {
212
+ await logSkip(projectRoot2);
213
+ return true;
214
+ }
215
+ const config = await loadHookConfig(projectRoot2);
216
+ if (!config?.hooks["pre-commit"]["rule-check"]?.enabled) {
217
+ return true;
218
+ }
219
+ const { validation_patterns } = config.hooks["pre-commit"]["rule-check"];
220
+ const diff = getCachedDiff();
221
+ const validationFiles = diff.filter(
222
+ (e) => isValidationFile(e.path, validation_patterns)
223
+ );
224
+ if (validationFiles.length === 0) return true;
225
+ const allDetections = [];
226
+ for (const file of validationFiles) {
227
+ const content = getCachedFileContent(file.path);
228
+ if (content) {
229
+ allDetections.push(...detectValidationPatterns(content, file.path));
230
+ }
231
+ }
232
+ if (allDetections.length === 0) return true;
233
+ for (const detection of allDetections) {
234
+ const matched = await hasMatchingRule(projectRoot2, detection);
235
+ if (!matched) {
236
+ printWarn(
237
+ "\u26A0\uFE0F New Validation Detected",
238
+ `File: ${detection.file}:${detection.line}
239
+ Pattern: ${detection.pattern}
240
+ Content: ${detection.content}
241
+
242
+ No matching BR-xxx in business-rules.md
243
+ Consider registering this rule.`
244
+ );
245
+ }
246
+ }
247
+ return true;
248
+ }
249
+
250
+ // src/hooks/claude-pretool-entry.ts
251
+ var projectRoot = resolve(".");
252
+ async function main() {
253
+ const chunks = [];
254
+ for await (const chunk of process.stdin) {
255
+ chunks.push(chunk);
256
+ }
257
+ const stdinData = Buffer.concat(chunks).toString("utf-8");
258
+ const hookInput = parseHookInput(stdinData);
259
+ if (!hookInput) {
260
+ process.exit(0);
261
+ }
262
+ const schemaDriftOk = await runSchemaDriftFromHookInput(
263
+ projectRoot,
264
+ hookInput
265
+ );
266
+ if (!schemaDriftOk) {
267
+ const result = {
268
+ decision: "block",
269
+ reason: "Schema drift detected. Update specs/entity-catalog.md first."
270
+ };
271
+ process.stdout.write(JSON.stringify(result));
272
+ process.exit(0);
273
+ }
274
+ await runRuleCheck(projectRoot);
275
+ }
276
+ main().catch((err) => {
277
+ console.error("IDDD claude-pretool hook error:", err);
278
+ process.exit(0);
279
+ });
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/hooks/post-commit-entry.ts
4
+ import { resolve } from "node:path";
5
+
6
+ // src/hooks/auto-audit.ts
7
+ import { readFile as readFile2, writeFile, mkdir } from "node:fs/promises";
8
+ import { join as join2 } from "node:path";
9
+
10
+ // src/hooks/shared.ts
11
+ import { readFile } from "node:fs/promises";
12
+ import { join } from "node:path";
13
+ import { execSync } from "node:child_process";
14
+
15
+ // src/utils/ascii.ts
16
+ function box(content, options = {}) {
17
+ const { title, width = 47, padding = 1 } = options;
18
+ const lines = content.split("\n");
19
+ const innerWidth = width - 2;
20
+ const padStr = " ".repeat(padding);
21
+ const result = [];
22
+ if (title) {
23
+ const titleStr = ` ${title} `;
24
+ const remaining = innerWidth - titleStr.length - 1;
25
+ result.push(` \u250C\u2500${titleStr}${"\u2500".repeat(Math.max(0, remaining))}\u2510`);
26
+ } else {
27
+ result.push(` \u250C${"\u2500".repeat(innerWidth)}\u2510`);
28
+ }
29
+ result.push(` \u2502${" ".repeat(innerWidth)}\u2502`);
30
+ for (const line of lines) {
31
+ const padded = `${padStr}${line}`;
32
+ const spaces = innerWidth - padded.length;
33
+ result.push(` \u2502${padded}${" ".repeat(Math.max(0, spaces))}\u2502`);
34
+ }
35
+ result.push(` \u2502${" ".repeat(innerWidth)}\u2502`);
36
+ result.push(` \u2514${"\u2500".repeat(innerWidth)}\u2518`);
37
+ return result.join("\n");
38
+ }
39
+
40
+ // src/hooks/shared.ts
41
+ async function loadHookConfig(projectRoot2) {
42
+ try {
43
+ const configPath = join(
44
+ projectRoot2,
45
+ ".claude",
46
+ "hooks",
47
+ "hook-config.json"
48
+ );
49
+ const content = await readFile(configPath, "utf-8");
50
+ return JSON.parse(content);
51
+ } catch {
52
+ return null;
53
+ }
54
+ }
55
+ function isSkipHooks() {
56
+ return process.env["IDDD_SKIP_HOOKS"] === "1";
57
+ }
58
+ async function logSkip(projectRoot2) {
59
+ const { appendFile, mkdir: mkdir2 } = await import("node:fs/promises");
60
+ const logDir = join(projectRoot2, ".iddd");
61
+ await mkdir2(logDir, { recursive: true });
62
+ const logPath = join(logDir, "skip-history.log");
63
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
64
+ let commitHash = "unknown";
65
+ try {
66
+ commitHash = execSync("git rev-parse HEAD", { encoding: "utf-8" }).trim();
67
+ } catch {
68
+ }
69
+ await appendFile(logPath, `${timestamp} ${commitHash} Hook skipped
70
+ `);
71
+ }
72
+ function printWarn(title, message) {
73
+ console.error(box(message, { title }));
74
+ }
75
+
76
+ // src/hooks/auto-audit.ts
77
+ async function incrementCounter(projectRoot2) {
78
+ const counterPath = join2(projectRoot2, ".iddd", "commit-count");
79
+ let count = 0;
80
+ try {
81
+ const content = await readFile2(counterPath, "utf-8");
82
+ count = parseInt(content.trim(), 10) || 0;
83
+ } catch {
84
+ }
85
+ count++;
86
+ await mkdir(join2(projectRoot2, ".iddd"), { recursive: true });
87
+ await writeFile(counterPath, count.toString());
88
+ return count;
89
+ }
90
+ async function resetCounter(projectRoot2) {
91
+ const counterPath = join2(projectRoot2, ".iddd", "commit-count");
92
+ await writeFile(counterPath, "0");
93
+ }
94
+ async function runAutoAudit(projectRoot2) {
95
+ if (isSkipHooks()) {
96
+ await logSkip(projectRoot2);
97
+ return;
98
+ }
99
+ const config = await loadHookConfig(projectRoot2);
100
+ if (!config?.hooks["post-commit"]["auto-audit"]?.enabled) {
101
+ return;
102
+ }
103
+ const threshold = config.hooks["post-commit"]["auto-audit"].interval_commits;
104
+ const count = await incrementCounter(projectRoot2);
105
+ if (count >= threshold) {
106
+ printWarn(
107
+ "\u2139\uFE0F Auto-Audit Triggered",
108
+ `${count} commits since last audit.
109
+ Run /id3-info-audit for a full check.
110
+
111
+ Report saved to .iddd/last-audit-report.md`
112
+ );
113
+ await resetCounter(projectRoot2);
114
+ }
115
+ }
116
+
117
+ // src/hooks/post-commit-entry.ts
118
+ var projectRoot = resolve(".");
119
+ runAutoAudit(projectRoot).catch((err) => {
120
+ console.error("IDDD post-commit hook error:", err);
121
+ });