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,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env node
2
+ import { initProject, printBanner, printSuccess } from '../src/init.js';
3
+ import { createInterface } from 'node:readline';
4
+ const args = process.argv.slice(2);
5
+ const flags = args.filter((a) => a.startsWith('--'));
6
+ const positional = args.filter((a) => !a.startsWith('--'));
7
+ // npx id3@latest [target-dir] — no subcommand needed
8
+ // npx id3@latest init [target-dir] — also works
9
+ const hasInit = positional[0] === 'init';
10
+ const targetDir = hasInit ? (positional[1] ?? '.') : (positional[0] ?? '.');
11
+ const noSymlink = flags.includes('--no-symlink');
12
+ {
13
+ printBanner();
14
+ const result = await initProject(targetDir, { noSymlink });
15
+ if (result.alreadyInstalled) {
16
+ const answer = await prompt('IDDD appears to be already installed. Overwrite? (y/N) ');
17
+ if (answer.toLowerCase() !== 'y') {
18
+ console.log('Aborted.');
19
+ process.exit(0);
20
+ }
21
+ const retryResult = await initProject(targetDir, {
22
+ overwrite: true,
23
+ noSymlink,
24
+ });
25
+ printSuccess(retryResult);
26
+ }
27
+ else {
28
+ printSuccess(result);
29
+ }
30
+ }
31
+ function prompt(question) {
32
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
33
+ return new Promise((resolve) => {
34
+ rl.question(question, (answer) => {
35
+ rl.close();
36
+ resolve(answer);
37
+ });
38
+ });
39
+ }
40
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../bin/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACnC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AACrD,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAE3D,sDAAsD;AACtD,iDAAiD;AACjD,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,MAAM,CAAC;AACzC,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;AAC5E,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,cAAc,CAAC,CAAC;AAEjD,CAAC;IAEC,WAAW,EAAE,CAAC;IAEd,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;IAE3D,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,MAAM,CACzB,yDAAyD,CAC1D,CAAC;QACF,IAAI,MAAM,CAAC,WAAW,EAAE,KAAK,GAAG,EAAE,CAAC;YACjC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACxB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,WAAW,GAAG,MAAM,WAAW,CAAC,SAAS,EAAE;YAC/C,SAAS,EAAE,IAAI;YACf,SAAS;SACV,CAAC,CAAC;QACH,YAAY,CAAC,WAAW,CAAC,CAAC;IAC5B,CAAC;SAAM,CAAC;QACN,YAAY,CAAC,MAAM,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,SAAS,MAAM,CAAC,QAAgB;IAC9B,MAAM,EAAE,GAAG,eAAe,CAAC,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7E,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,EAAE;YAC/B,EAAE,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,CAAC,MAAM,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ import { build } from 'esbuild';
2
+ import { chmod } from 'node:fs/promises';
3
+ import { join } from 'node:path';
4
+ // When compiled, this runs from dist/scripts/, so go up 2 levels to project root
5
+ const outDir = join(import.meta.dirname, '..', '..', 'templates', 'hooks');
6
+ async function buildHooks() {
7
+ // Git Hook: Pre-commit (schema-drift + rule-check)
8
+ await build({
9
+ entryPoints: ['src/hooks/pre-commit-entry.ts'],
10
+ bundle: true,
11
+ platform: 'node',
12
+ target: 'node18',
13
+ format: 'esm',
14
+ outfile: join(outDir, 'pre-commit'),
15
+ banner: { js: '#!/usr/bin/env node' },
16
+ });
17
+ // Git Hook: Post-commit (auto-audit)
18
+ await build({
19
+ entryPoints: ['src/hooks/post-commit-entry.ts'],
20
+ bundle: true,
21
+ platform: 'node',
22
+ target: 'node18',
23
+ format: 'esm',
24
+ outfile: join(outDir, 'post-commit'),
25
+ banner: { js: '#!/usr/bin/env node' },
26
+ });
27
+ // Claude Code Hook: PreToolUse (schema-drift + rule-check)
28
+ await build({
29
+ entryPoints: ['src/hooks/claude-pretool-entry.ts'],
30
+ bundle: true,
31
+ platform: 'node',
32
+ target: 'node18',
33
+ format: 'esm',
34
+ outfile: join(outDir, 'iddd-schema-drift.js'),
35
+ banner: { js: '#!/usr/bin/env node' },
36
+ });
37
+ // Claude Code Hook: Stop (auto-audit)
38
+ await build({
39
+ entryPoints: ['src/hooks/claude-stop-entry.ts'],
40
+ bundle: true,
41
+ platform: 'node',
42
+ target: 'node18',
43
+ format: 'esm',
44
+ outfile: join(outDir, 'iddd-auto-audit.js'),
45
+ banner: { js: '#!/usr/bin/env node' },
46
+ });
47
+ // Make all bundles executable
48
+ await chmod(join(outDir, 'pre-commit'), 0o755);
49
+ await chmod(join(outDir, 'post-commit'), 0o755);
50
+ await chmod(join(outDir, 'iddd-schema-drift.js'), 0o755);
51
+ await chmod(join(outDir, 'iddd-auto-audit.js'), 0o755);
52
+ console.log('✓ Hooks built successfully (Git + Claude Code)');
53
+ }
54
+ buildHooks().catch((err) => {
55
+ console.error('Hook build failed:', err);
56
+ process.exit(1);
57
+ });
58
+ //# sourceMappingURL=build-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-hooks.js","sourceRoot":"","sources":["../../scripts/build-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,iFAAiF;AACjF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;AAE3E,KAAK,UAAU,UAAU;IACvB,mDAAmD;IACnD,MAAM,KAAK,CAAC;QACV,WAAW,EAAE,CAAC,+BAA+B,CAAC;QAC9C,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;QACnC,MAAM,EAAE,EAAE,EAAE,EAAE,qBAAqB,EAAE;KACtC,CAAC,CAAC;IAEH,qCAAqC;IACrC,MAAM,KAAK,CAAC;QACV,WAAW,EAAE,CAAC,gCAAgC,CAAC;QAC/C,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC;QACpC,MAAM,EAAE,EAAE,EAAE,EAAE,qBAAqB,EAAE;KACtC,CAAC,CAAC;IAEH,2DAA2D;IAC3D,MAAM,KAAK,CAAC;QACV,WAAW,EAAE,CAAC,mCAAmC,CAAC;QAClD,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,sBAAsB,CAAC;QAC7C,MAAM,EAAE,EAAE,EAAE,EAAE,qBAAqB,EAAE;KACtC,CAAC,CAAC;IAEH,sCAAsC;IACtC,MAAM,KAAK,CAAC;QACV,WAAW,EAAE,CAAC,gCAAgC,CAAC;QAC/C,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,QAAQ;QAChB,MAAM,EAAE,KAAK;QACb,OAAO,EAAE,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC;QAC3C,MAAM,EAAE,EAAE,EAAE,EAAE,qBAAqB,EAAE;KACtC,CAAC,CAAC;IAEH,8BAA8B;IAC9B,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,KAAK,CAAC,CAAC;IAC/C,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,sBAAsB,CAAC,EAAE,KAAK,CAAC,CAAC;IACzD,MAAM,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAAE,KAAK,CAAC,CAAC;IAEvD,OAAO,CAAC,GAAG,CAAC,gDAAgD,CAAC,CAAC;AAChE,CAAC;AAED,UAAU,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACzB,OAAO,CAAC,KAAK,CAAC,oBAAoB,EAAE,GAAG,CAAC,CAAC;IACzC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function incrementCounter(projectRoot: string): Promise<number>;
2
+ export declare function shouldRunAudit(projectRoot: string, threshold: number): Promise<boolean>;
3
+ export declare function resetCounter(projectRoot: string): Promise<void>;
4
+ export declare function runAutoAudit(projectRoot: string): Promise<void>;
@@ -0,0 +1,47 @@
1
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { isSkipHooks, logSkip, loadHookConfig, printWarn } from './shared.js';
4
+ export async function incrementCounter(projectRoot) {
5
+ const counterPath = join(projectRoot, '.iddd', 'commit-count');
6
+ let count = 0;
7
+ try {
8
+ const content = await readFile(counterPath, 'utf-8');
9
+ count = parseInt(content.trim(), 10) || 0;
10
+ }
11
+ catch { }
12
+ count++;
13
+ await mkdir(join(projectRoot, '.iddd'), { recursive: true });
14
+ await writeFile(counterPath, count.toString());
15
+ return count;
16
+ }
17
+ export async function shouldRunAudit(projectRoot, threshold) {
18
+ const counterPath = join(projectRoot, '.iddd', 'commit-count');
19
+ try {
20
+ const content = await readFile(counterPath, 'utf-8');
21
+ return parseInt(content.trim(), 10) >= threshold;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ export async function resetCounter(projectRoot) {
28
+ const counterPath = join(projectRoot, '.iddd', 'commit-count');
29
+ await writeFile(counterPath, '0');
30
+ }
31
+ export async function runAutoAudit(projectRoot) {
32
+ if (isSkipHooks()) {
33
+ await logSkip(projectRoot);
34
+ return;
35
+ }
36
+ const config = await loadHookConfig(projectRoot);
37
+ if (!config?.hooks['post-commit']['auto-audit']?.enabled) {
38
+ return;
39
+ }
40
+ const threshold = config.hooks['post-commit']['auto-audit'].interval_commits;
41
+ const count = await incrementCounter(projectRoot);
42
+ if (count >= threshold) {
43
+ printWarn('ℹ️ Auto-Audit Triggered', `${count} commits since last audit.\nRun /id3-info-audit for a full check.\n\nReport saved to .iddd/last-audit-report.md`);
44
+ await resetCounter(projectRoot);
45
+ }
46
+ }
47
+ //# sourceMappingURL=auto-audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-audit.js","sourceRoot":"","sources":["../../../src/hooks/auto-audit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE9E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,WAAmB;IACxD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;IAC/D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACrD,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;IACV,KAAK,EAAE,CAAC;IACR,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,SAAS,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC/C,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,WAAmB,EACnB,SAAiB;IAEjB,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;IAC/D,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACrD,OAAO,QAAQ,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;IACnD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB;IACpD,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,OAAO,EAAE,cAAc,CAAC,CAAC;IAC/D,MAAM,SAAS,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,WAAmB;IACpD,IAAI,WAAW,EAAE,EAAE,CAAC;QAClB,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;QAC3B,OAAO;IACT,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;IACjD,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC;QACzD,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,YAAY,CAAC,CAAC,gBAAgB,CAAC;IAC7E,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAElD,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;QACvB,SAAS,CACP,yBAAyB,EACzB,GAAG,KAAK,iHAAiH,CAC1H,CAAC;QACF,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;IAClC,CAAC;AACH,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,36 @@
1
+ import { resolve } from 'node:path';
2
+ import { parseHookInput } from './shared.js';
3
+ import { runSchemaDriftFromHookInput } from './schema-drift.js';
4
+ import { runRuleCheck } from './rule-check.js';
5
+ const projectRoot = resolve('.');
6
+ async function main() {
7
+ // Read stdin JSON from Claude Code Hook API
8
+ const chunks = [];
9
+ for await (const chunk of process.stdin) {
10
+ chunks.push(chunk);
11
+ }
12
+ const stdinData = Buffer.concat(chunks).toString('utf-8');
13
+ const hookInput = parseHookInput(stdinData);
14
+ if (!hookInput) {
15
+ // Not a valid hook input — pass through
16
+ process.exit(0);
17
+ }
18
+ // schema-drift check
19
+ const schemaDriftOk = await runSchemaDriftFromHookInput(projectRoot, hookInput);
20
+ if (!schemaDriftOk) {
21
+ // Output error JSON for Claude Code Hook API
22
+ const result = {
23
+ decision: 'block',
24
+ reason: 'Schema drift detected. Update specs/entity-catalog.md first.',
25
+ };
26
+ process.stdout.write(JSON.stringify(result));
27
+ process.exit(0);
28
+ }
29
+ // rule-check (warning only, does not block)
30
+ await runRuleCheck(projectRoot);
31
+ }
32
+ main().catch((err) => {
33
+ console.error('IDDD claude-pretool hook error:', err);
34
+ process.exit(0); // don't block on errors
35
+ });
36
+ //# sourceMappingURL=claude-pretool-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-pretool-entry.js","sourceRoot":"","sources":["../../../src/hooks/claude-pretool-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,2BAA2B,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAEjC,KAAK,UAAU,IAAI;IACjB,4CAA4C;IAC5C,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE1D,MAAM,SAAS,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IAC5C,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,wCAAwC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,qBAAqB;IACrB,MAAM,aAAa,GAAG,MAAM,2BAA2B,CACrD,WAAW,EACX,SAAS,CACV,CAAC;IACF,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,6CAA6C;QAC7C,MAAM,MAAM,GAAG;YACb,QAAQ,EAAE,OAAO;YACjB,MAAM,EAAE,8DAA8D;SACvE,CAAC;QACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;QAC7C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,4CAA4C;IAC5C,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;IACtD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;AAC3C,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { resolve } from 'node:path';
2
+ import { runAutoAudit } from './auto-audit.js';
3
+ const projectRoot = resolve('.');
4
+ runAutoAudit(projectRoot).catch((err) => {
5
+ console.error('IDDD claude-stop hook error:', err);
6
+ });
7
+ //# sourceMappingURL=claude-stop-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-stop-entry.js","sourceRoot":"","sources":["../../../src/hooks/claude-stop-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAEjC,YAAY,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACtC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { resolve } from 'node:path';
2
+ import { runAutoAudit } from './auto-audit.js';
3
+ const projectRoot = resolve('.');
4
+ runAutoAudit(projectRoot).catch((err) => {
5
+ console.error('IDDD post-commit hook error:', err);
6
+ });
7
+ //# sourceMappingURL=post-commit-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-commit-entry.js","sourceRoot":"","sources":["../../../src/hooks/post-commit-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAEjC,YAAY,CAAC,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACtC,OAAO,CAAC,KAAK,CAAC,8BAA8B,EAAE,GAAG,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
1
+ import { resolve } from 'node:path';
2
+ import { runSchemaDrift } from './schema-drift.js';
3
+ import { runRuleCheck } from './rule-check.js';
4
+ const projectRoot = resolve('.');
5
+ async function main() {
6
+ const schemaDriftOk = await runSchemaDrift(projectRoot);
7
+ if (!schemaDriftOk) {
8
+ process.exit(1);
9
+ }
10
+ await runRuleCheck(projectRoot);
11
+ }
12
+ main().catch((err) => {
13
+ console.error('IDDD pre-commit hook error:', err);
14
+ process.exit(1);
15
+ });
16
+ //# sourceMappingURL=pre-commit-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pre-commit-entry.js","sourceRoot":"","sources":["../../../src/hooks/pre-commit-entry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAE/C,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;AAEjC,KAAK,UAAU,IAAI;IACjB,MAAM,aAAa,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;IACxD,IAAI,CAAC,aAAa,EAAE,CAAC;QACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,GAAG,CAAC,CAAC;IAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface ValidationDetection {
2
+ file: string;
3
+ line: number;
4
+ content: string;
5
+ pattern: string;
6
+ }
7
+ export declare function detectValidationPatterns(content: string, filePath: string): ValidationDetection[];
8
+ export declare function runRuleCheck(projectRoot: string): Promise<boolean>;
@@ -0,0 +1,101 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { getCachedDiff, getCachedFileContent, isValidationFile, isSkipHooks, logSkip, loadHookConfig, printWarn, } from './shared.js';
4
+ const VALIDATION_PATTERNS = [
5
+ {
6
+ regex: /z\.\s*(?:object|string|number|boolean|array|enum)\s*\(/g,
7
+ label: 'zod',
8
+ },
9
+ {
10
+ regex: /yup\.\s*(?:object|string|number|boolean|array)\s*\(/g,
11
+ label: 'yup',
12
+ },
13
+ {
14
+ regex: /Joi\.\s*(?:object|string|number|boolean|array)\s*\(/g,
15
+ label: 'joi',
16
+ },
17
+ { regex: /CHECK\s*\(/gi, label: 'SQL CHECK' },
18
+ { regex: /NOT\s+NULL/gi, label: 'SQL NOT NULL' },
19
+ { regex: /ADD\s+.*UNIQUE/gi, label: 'SQL UNIQUE' },
20
+ { regex: /@validator\s*\(/g, label: 'pydantic' },
21
+ { regex: /@field_validator\s*\(/g, label: 'pydantic-v2' },
22
+ { regex: /@Valid/g, label: 'java-valid' },
23
+ { regex: /@NotNull/g, label: 'java-notnull' },
24
+ {
25
+ regex: /@Column\s*\(\s*.*nullable\s*[:=]\s*false/g,
26
+ label: 'orm-notnull',
27
+ },
28
+ { regex: /@IsNotEmpty\s*\(/g, label: 'class-validator' },
29
+ {
30
+ regex: /body\s*\(\s*['"].*['"]\s*\)\s*\.(?:not|is)/g,
31
+ label: 'express-validator',
32
+ },
33
+ ];
34
+ export function detectValidationPatterns(content, filePath) {
35
+ const detections = [];
36
+ const lines = content.split('\n');
37
+ for (let i = 0; i < lines.length; i++) {
38
+ const line = lines[i];
39
+ if (!line.startsWith('+') && content.includes('\n+')) {
40
+ // only check added lines if it looks like a diff
41
+ continue;
42
+ }
43
+ const cleanLine = line.replace(/^\+/, '');
44
+ for (const { regex, label } of VALIDATION_PATTERNS) {
45
+ regex.lastIndex = 0;
46
+ if (regex.test(cleanLine)) {
47
+ detections.push({
48
+ file: filePath,
49
+ line: i + 1,
50
+ content: cleanLine.trim(),
51
+ pattern: label,
52
+ });
53
+ }
54
+ }
55
+ }
56
+ return detections;
57
+ }
58
+ async function hasMatchingRule(projectRoot, detection) {
59
+ try {
60
+ const rulesPath = join(projectRoot, 'docs', 'business-rules.md');
61
+ const rules = await readFile(rulesPath, 'utf-8');
62
+ // Simple check: look for BR-xxx entries that reference the same file/pattern
63
+ return rules.includes('BR-') && rules.length > 200;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ export async function runRuleCheck(projectRoot) {
70
+ if (isSkipHooks()) {
71
+ await logSkip(projectRoot);
72
+ return true;
73
+ }
74
+ const config = await loadHookConfig(projectRoot);
75
+ if (!config?.hooks['pre-commit']['rule-check']?.enabled) {
76
+ return true;
77
+ }
78
+ const { validation_patterns } = config.hooks['pre-commit']['rule-check'];
79
+ const diff = getCachedDiff();
80
+ const validationFiles = diff.filter((e) => isValidationFile(e.path, validation_patterns));
81
+ if (validationFiles.length === 0)
82
+ return true;
83
+ const allDetections = [];
84
+ for (const file of validationFiles) {
85
+ const content = getCachedFileContent(file.path);
86
+ if (content) {
87
+ allDetections.push(...detectValidationPatterns(content, file.path));
88
+ }
89
+ }
90
+ if (allDetections.length === 0)
91
+ return true;
92
+ // Check if rules exist
93
+ for (const detection of allDetections) {
94
+ const matched = await hasMatchingRule(projectRoot, detection);
95
+ if (!matched) {
96
+ printWarn('⚠️ New Validation Detected', `File: ${detection.file}:${detection.line}\nPattern: ${detection.pattern}\nContent: ${detection.content}\n\nNo matching BR-xxx in business-rules.md\nConsider registering this rule.`);
97
+ }
98
+ }
99
+ return true; // never blocks, warning only
100
+ }
101
+ //# sourceMappingURL=rule-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-check.js","sourceRoot":"","sources":["../../../src/hooks/rule-check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { type HookInput } from './shared.js';
2
+ export interface SchemaChange {
3
+ type: 'add' | 'modify' | 'drop';
4
+ table: string;
5
+ column?: string;
6
+ trivial: boolean;
7
+ description: string;
8
+ }
9
+ export declare function analyzeSchemaChanges(diffContent: string, filePath: string): SchemaChange[];
10
+ /**
11
+ * Git Hook mode: extract file list from git diff --cached
12
+ */
13
+ export declare function runSchemaDrift(projectRoot: string): Promise<boolean>;
14
+ /**
15
+ * Claude Code Hook mode: receive { tool_name, tool_input: { file_path } } JSON from stdin
16
+ */
17
+ export declare function runSchemaDriftFromHookInput(projectRoot: string, hookInput: HookInput): Promise<boolean>;
@@ -0,0 +1,151 @@
1
+ import { getCachedDiff, getCachedFileContent, isSchemaFile, isSkipHooks, logSkip, loadHookConfig, printBlock, printWarn, } from './shared.js';
2
+ export function analyzeSchemaChanges(diffContent, filePath) {
3
+ const changes = [];
4
+ const addedLines = diffContent
5
+ .split('\n')
6
+ .filter((l) => l.startsWith('+') && !l.startsWith('+++'))
7
+ .map((l) => l.slice(1).trim());
8
+ for (const line of addedLines) {
9
+ // SQL: CREATE TABLE
10
+ const createMatch = line.match(/CREATE\s+TABLE\s+(\w+)/i);
11
+ if (createMatch) {
12
+ changes.push({
13
+ type: 'add',
14
+ table: createMatch[1],
15
+ trivial: false,
16
+ description: `Table ${createMatch[1]} created`,
17
+ });
18
+ continue;
19
+ }
20
+ // SQL: ALTER TABLE ... ADD COLUMN
21
+ const alterAddMatch = line.match(/ALTER\s+TABLE\s+(\w+)\s+ADD\s+(?:COLUMN\s+)?(\w+)/i);
22
+ if (alterAddMatch) {
23
+ changes.push({
24
+ type: 'add',
25
+ table: alterAddMatch[1],
26
+ column: alterAddMatch[2],
27
+ trivial: false,
28
+ description: `Column ${alterAddMatch[1]}.${alterAddMatch[2]} added`,
29
+ });
30
+ continue;
31
+ }
32
+ // SQL: DROP TABLE
33
+ const dropMatch = line.match(/DROP\s+TABLE\s+(\w+)/i);
34
+ if (dropMatch) {
35
+ changes.push({
36
+ type: 'drop',
37
+ table: dropMatch[1],
38
+ trivial: false,
39
+ description: `Table ${dropMatch[1]} dropped`,
40
+ });
41
+ continue;
42
+ }
43
+ // SQL: CREATE INDEX (trivial)
44
+ const indexMatch = line.match(/CREATE\s+(?:UNIQUE\s+)?INDEX\s+/i);
45
+ if (indexMatch) {
46
+ const tableMatch = line.match(/ON\s+(\w+)/i);
47
+ changes.push({
48
+ type: 'modify',
49
+ table: tableMatch?.[1] ?? 'unknown',
50
+ trivial: true,
51
+ description: `Index added`,
52
+ });
53
+ continue;
54
+ }
55
+ // Prisma: model definition
56
+ const prismaModel = line.match(/^model\s+(\w+)\s*\{/);
57
+ if (prismaModel) {
58
+ changes.push({
59
+ type: 'add',
60
+ table: prismaModel[1],
61
+ trivial: false,
62
+ description: `Model ${prismaModel[1]} added`,
63
+ });
64
+ continue;
65
+ }
66
+ // Django: class Model(models.Model)
67
+ const djangoModel = line.match(/^class\s+(\w+)\(.*Model.*\)/);
68
+ if (djangoModel) {
69
+ changes.push({
70
+ type: 'add',
71
+ table: djangoModel[1],
72
+ trivial: false,
73
+ description: `Model ${djangoModel[1]} added`,
74
+ });
75
+ continue;
76
+ }
77
+ }
78
+ return changes;
79
+ }
80
+ /**
81
+ * Git Hook mode: extract file list from git diff --cached
82
+ */
83
+ export async function runSchemaDrift(projectRoot) {
84
+ if (isSkipHooks()) {
85
+ await logSkip(projectRoot);
86
+ return true; // allow
87
+ }
88
+ const config = await loadHookConfig(projectRoot);
89
+ if (!config?.hooks['pre-commit']['schema-drift']?.enabled) {
90
+ return true;
91
+ }
92
+ const { monitored_patterns } = config.hooks['pre-commit']['schema-drift'];
93
+ const diff = getCachedDiff();
94
+ const schemaFiles = diff.filter((e) => isSchemaFile(e.path, monitored_patterns));
95
+ if (schemaFiles.length === 0)
96
+ return true;
97
+ // Check if entity-catalog.md is also being committed
98
+ const catalogModified = diff.some((e) => e.path === 'specs/entity-catalog.md');
99
+ // Analyze changes
100
+ const allChanges = [];
101
+ for (const file of schemaFiles) {
102
+ const content = getCachedFileContent(file.path);
103
+ if (content) {
104
+ allChanges.push(...analyzeSchemaChanges(content, file.path));
105
+ }
106
+ }
107
+ if (allChanges.length === 0)
108
+ return true;
109
+ const hasStructural = allChanges.some((c) => !c.trivial);
110
+ if (catalogModified) {
111
+ return true; // entity-catalog.md updated together — OK
112
+ }
113
+ if (!hasStructural) {
114
+ // Only trivial changes (indexes, etc.)
115
+ printWarn('⚠️ Schema Change Detected', allChanges.map((c) => c.description).join('\n') +
116
+ '\n\nConsider updating specs/entity-catalog.md');
117
+ return true;
118
+ }
119
+ // Structural change without entity-catalog.md update — BLOCK
120
+ const changeList = allChanges
121
+ .filter((c) => !c.trivial)
122
+ .map((c) => ` ${c.description}`)
123
+ .join('\n');
124
+ printBlock('❌ Schema Drift Detected', `entity-catalog.md must be updated first.\n\n${changeList}\n\nRun /id3-info-audit or manually update\nspecs/entity-catalog.md`);
125
+ return false; // block commit
126
+ }
127
+ /**
128
+ * Claude Code Hook mode: receive { tool_name, tool_input: { file_path } } JSON from stdin
129
+ */
130
+ export async function runSchemaDriftFromHookInput(projectRoot, hookInput) {
131
+ if (isSkipHooks()) {
132
+ await logSkip(projectRoot);
133
+ return true;
134
+ }
135
+ const config = await loadHookConfig(projectRoot);
136
+ if (!config?.hooks['pre-commit']['schema-drift']?.enabled) {
137
+ return true;
138
+ }
139
+ const { monitored_patterns } = config.hooks['pre-commit']['schema-drift'];
140
+ // Extract relative path from absolute file_path
141
+ const relativePath = hookInput.filePath.startsWith(projectRoot)
142
+ ? hookInput.filePath.slice(projectRoot.length + 1)
143
+ : hookInput.filePath;
144
+ if (!isSchemaFile(relativePath, monitored_patterns)) {
145
+ return true; // not a schema file — allow
146
+ }
147
+ // Schema file is being written/edited — check if entity-catalog.md exists and is recent
148
+ printWarn('⚠️ Schema File Modified', `File: ${relativePath}\n\nEnsure specs/entity-catalog.md is updated\nto reflect this schema change.`);
149
+ return true; // warn only in Claude Code Hook mode (non-blocking)
150
+ }
151
+ //# sourceMappingURL=schema-drift.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-drift.js","sourceRoot":"","sources":["../../../src/hooks/schema-drift.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ export interface DiffEntry {
2
+ status: string;
3
+ path: string;
4
+ }
5
+ export declare function parseGitDiff(output: string): DiffEntry[];
6
+ export interface HookInput {
7
+ toolName: string;
8
+ filePath: string;
9
+ }
10
+ export declare function parseHookInput(stdinData: string): HookInput | null;
11
+ export declare function getCachedDiff(): DiffEntry[];
12
+ export declare function getCachedFileContent(filePath: string): string | null;
13
+ export declare function isSchemaFile(filePath: string, patterns: string[]): boolean;
14
+ export declare function isValidationFile(filePath: string, patterns: string[]): boolean;
15
+ export declare function matchGlob(filePath: string, pattern: string): boolean;
16
+ export interface HookConfig {
17
+ enabled: boolean;
18
+ hooks: {
19
+ 'pre-commit': {
20
+ 'schema-drift': {
21
+ enabled: boolean;
22
+ severity: 'block' | 'warn';
23
+ monitored_patterns: string[];
24
+ };
25
+ 'rule-check': {
26
+ enabled: boolean;
27
+ severity: 'block' | 'warn';
28
+ validation_patterns: string[];
29
+ };
30
+ };
31
+ 'post-commit': {
32
+ 'auto-audit': {
33
+ enabled: boolean;
34
+ interval_commits: number;
35
+ };
36
+ };
37
+ };
38
+ }
39
+ export declare function loadHookConfig(projectRoot: string): Promise<HookConfig | null>;
40
+ export declare function isSkipHooks(): boolean;
41
+ export declare function logSkip(projectRoot: string): Promise<void>;
42
+ export declare function printBlock(title: string, message: string): void;
43
+ export declare function printWarn(title: string, message: string): void;