@elevasis/sdk 1.20.2 → 1.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/dist/cli.cjs +4220 -1583
  2. package/dist/index.d.ts +1035 -481
  3. package/dist/index.js +7381 -4187
  4. package/dist/node/index.d.ts +1 -3
  5. package/dist/node/index.js +40 -49
  6. package/dist/test-utils/index.d.ts +699 -123
  7. package/dist/test-utils/index.js +3826 -630
  8. package/dist/worker/index.js +3616 -442
  9. package/package.json +3 -3
  10. package/reference/_navigation.md +9 -7
  11. package/reference/_reference-manifest.json +1 -1
  12. package/reference/claude-config/hooks/post-edit-validate.mjs +98 -98
  13. package/reference/claude-config/hooks/scaffold-registry-reminder.mjs +188 -188
  14. package/reference/claude-config/hooks/tool-failure-recovery.mjs +73 -73
  15. package/reference/claude-config/registries/graph-skills.json +4 -4
  16. package/reference/claude-config/registries/knowledge-flags.json +0 -2
  17. package/reference/claude-config/rules/active-change-index.md +80 -80
  18. package/reference/claude-config/rules/agent-start-here.md +277 -273
  19. package/reference/claude-config/rules/deployment.md +57 -57
  20. package/reference/claude-config/rules/error-handling.md +56 -56
  21. package/reference/claude-config/rules/execution.md +40 -40
  22. package/reference/claude-config/rules/frontend.md +4 -4
  23. package/reference/claude-config/rules/observability.md +31 -31
  24. package/reference/claude-config/rules/operations.md +29 -17
  25. package/reference/claude-config/rules/organization-model.md +108 -40
  26. package/reference/claude-config/rules/organization-os.md +115 -113
  27. package/reference/claude-config/rules/package-taxonomy.md +33 -33
  28. package/reference/claude-config/rules/platform.md +42 -42
  29. package/reference/claude-config/rules/shared-types.md +49 -46
  30. package/reference/claude-config/rules/task-tracking.md +47 -47
  31. package/reference/claude-config/rules/ui.md +200 -200
  32. package/reference/claude-config/rules/vibe.md +235 -231
  33. package/reference/claude-config/scripts/statusline-command.js +18 -18
  34. package/reference/claude-config/settings.json +34 -34
  35. package/reference/claude-config/skills/deploy/{SKILL.md → skill.md} +156 -156
  36. package/reference/claude-config/skills/dsp/SKILL.md +66 -66
  37. package/reference/claude-config/skills/elevasis/SKILL.md +235 -235
  38. package/reference/claude-config/skills/explore/SKILL.md +6 -6
  39. package/reference/claude-config/skills/git-sync/SKILL.md +126 -126
  40. package/reference/claude-config/skills/knowledge/SKILL.md +330 -271
  41. package/reference/claude-config/skills/knowledge/operations/codify-level-a.md +100 -100
  42. package/reference/claude-config/skills/knowledge/operations/codify-level-b.md +159 -158
  43. package/reference/claude-config/skills/knowledge/operations/customers.md +109 -109
  44. package/reference/claude-config/skills/knowledge/operations/features.md +76 -113
  45. package/reference/claude-config/skills/knowledge/operations/goals.md +118 -118
  46. package/reference/claude-config/skills/knowledge/operations/identity.md +93 -93
  47. package/reference/claude-config/skills/knowledge/operations/labels.md +94 -89
  48. package/reference/claude-config/skills/knowledge/operations/offerings.md +109 -109
  49. package/reference/claude-config/skills/knowledge/operations/roles.md +99 -99
  50. package/reference/claude-config/skills/knowledge/operations/techStack.md +30 -30
  51. package/reference/claude-config/skills/project/SKILL.md +1088 -1088
  52. package/reference/claude-config/skills/run-ui/SKILL.md +73 -73
  53. package/reference/claude-config/skills/save/SKILL.md +3 -3
  54. package/reference/claude-config/skills/setup/SKILL.md +275 -275
  55. package/reference/claude-config/skills/status/SKILL.md +59 -59
  56. package/reference/claude-config/skills/submit-request/SKILL.md +180 -180
  57. package/reference/claude-config/skills/sync/SKILL.md +47 -47
  58. package/reference/claude-config/skills/tutorial/SKILL.md +259 -259
  59. package/reference/claude-config/skills/tutorial/progress-template.md +74 -74
  60. package/reference/claude-config/skills/tutorial/technical.md +1303 -1306
  61. package/reference/claude-config/skills/tutorial/vibe-coder.md +890 -890
  62. package/reference/claude-config/sync-notes/2026-04-22-git-sync-and-sync-notes.md +27 -27
  63. package/reference/claude-config/sync-notes/2026-04-22-lead-gen-deliverability-removal.md +30 -30
  64. package/reference/claude-config/sync-notes/2026-04-24-test-utils-and-template-tests.md +73 -73
  65. package/reference/claude-config/sync-notes/2026-04-24-ui-consolidation-and-sdk-cli-train.md +86 -86
  66. package/reference/claude-config/sync-notes/2026-04-25-auth-role-system-and-settings-roles.md +55 -55
  67. package/reference/claude-config/sync-notes/2026-04-27-crm-hitl-action-layer-cutover.md +97 -97
  68. package/reference/claude-config/sync-notes/2026-04-27-lead-gen-substrate-train.md +112 -112
  69. package/reference/claude-config/sync-notes/2026-04-29-crm-state-and-lead-gen-processing-status.md +93 -93
  70. package/reference/claude-config/sync-notes/2026-05-02-crm-ownership-next-action.md +58 -58
  71. package/reference/claude-config/sync-notes/2026-05-02-template-hardcode-workos-config.md +56 -56
  72. package/reference/claude-config/sync-notes/2026-05-04-elevasis-workspace.md +71 -71
  73. package/reference/claude-config/sync-notes/2026-05-04-knowledge-bundle.md +83 -83
  74. package/reference/claude-config/sync-notes/2026-05-04-template-skills-run-ui-and-tutorial.md +59 -59
  75. package/reference/claude-config/sync-notes/2026-05-05-list-builder.md +42 -42
  76. package/reference/claude-config/sync-notes/2026-05-06-crm-spine.md +60 -60
  77. package/reference/claude-config/sync-notes/2026-05-06-sdk-changes-release-train.md +37 -37
  78. package/reference/claude-config/sync-notes/2026-05-07-sdk-changes-release-train.md +34 -34
  79. package/reference/claude-config/sync-notes/2026-05-08-resource-governance-scaffold-guidance.md +38 -38
  80. package/reference/claude-config/sync-notes/2026-05-09-clients-domain.md +32 -32
  81. package/reference/claude-config/sync-notes/2026-05-09-command-system.md +33 -33
  82. package/reference/claude-config/sync-notes/2026-05-09-resource-governance-and-misc.md +69 -69
  83. package/reference/claude-config/sync-notes/2026-05-12-sdk-ready-release-train.md +30 -0
  84. package/reference/claude-config/sync-notes/2026-05-14-organization-model-ontology-refactor.md +42 -0
  85. package/reference/claude-config/sync-notes/README.md +43 -43
  86. package/reference/cli.mdx +808 -668
  87. package/reference/concepts.mdx +146 -146
  88. package/reference/deployment/api.mdx +297 -297
  89. package/reference/deployment/command-center.mdx +209 -209
  90. package/reference/deployment/index.mdx +195 -195
  91. package/reference/deployment/provided-features.mdx +107 -93
  92. package/reference/deployment/ui-execution.mdx +250 -250
  93. package/reference/examples/organization-model.ts +147 -84
  94. package/reference/framework/agent.mdx +156 -156
  95. package/reference/framework/index.mdx +195 -195
  96. package/reference/framework/interaction-guidance.mdx +182 -182
  97. package/reference/framework/memory.mdx +326 -326
  98. package/reference/framework/project-structure.mdx +282 -282
  99. package/reference/framework/tutorial-system.mdx +135 -135
  100. package/reference/getting-started.mdx +142 -142
  101. package/reference/index.mdx +106 -106
  102. package/reference/packages/core/src/README.md +14 -14
  103. package/reference/packages/core/src/business/README.md +2 -2
  104. package/reference/packages/core/src/knowledge/README.md +33 -32
  105. package/reference/packages/core/src/organization-model/README.md +149 -109
  106. package/reference/packages/core/src/test-utils/README.md +37 -37
  107. package/reference/packages/ui/src/api/README.md +18 -18
  108. package/reference/packages/ui/src/app/README.md +24 -24
  109. package/reference/packages/ui/src/auth/README.md +18 -18
  110. package/reference/packages/ui/src/components/README.md +24 -24
  111. package/reference/packages/ui/src/execution/README.md +16 -16
  112. package/reference/packages/ui/src/features/README.md +28 -28
  113. package/reference/packages/ui/src/graph/README.md +16 -16
  114. package/reference/packages/ui/src/hooks/README.md +23 -23
  115. package/reference/packages/ui/src/initialization/README.md +19 -19
  116. package/reference/packages/ui/src/knowledge/README.md +31 -31
  117. package/reference/packages/ui/src/organization/README.md +18 -18
  118. package/reference/packages/ui/src/profile/README.md +19 -19
  119. package/reference/packages/ui/src/provider/README.md +32 -32
  120. package/reference/packages/ui/src/router/README.md +18 -18
  121. package/reference/packages/ui/src/sse/README.md +13 -13
  122. package/reference/packages/ui/src/test-utils/README.md +7 -7
  123. package/reference/packages/ui/src/theme/README.md +23 -23
  124. package/reference/packages/ui/src/theme/presets/README.md +19 -19
  125. package/reference/packages/ui/src/types/README.md +16 -16
  126. package/reference/packages/ui/src/utils/README.md +18 -18
  127. package/reference/packages/ui/src/zustand/README.md +18 -18
  128. package/reference/platform-tools/adapters-integration.mdx +301 -301
  129. package/reference/platform-tools/adapters-platform.mdx +553 -553
  130. package/reference/platform-tools/index.mdx +217 -217
  131. package/reference/platform-tools/type-safety.mdx +82 -82
  132. package/reference/resources/index.mdx +349 -349
  133. package/reference/resources/patterns.mdx +449 -449
  134. package/reference/resources/types.mdx +116 -116
  135. package/reference/roadmap.mdx +165 -165
  136. package/reference/runtime.mdx +173 -173
  137. package/reference/scaffold/core/organization-graph.mdx +110 -89
  138. package/reference/scaffold/core/organization-model.mdx +226 -171
  139. package/reference/scaffold/index.mdx +67 -67
  140. package/reference/scaffold/operations/propagation-pipeline.md +77 -77
  141. package/reference/scaffold/operations/scaffold-maintenance.md +10 -10
  142. package/reference/scaffold/operations/workflow-recipes.md +138 -138
  143. package/reference/scaffold/recipes/add-a-feature.md +310 -88
  144. package/reference/scaffold/recipes/add-a-resource.md +137 -117
  145. package/reference/scaffold/recipes/customize-crm-actions.md +439 -439
  146. package/reference/scaffold/recipes/customize-knowledge-browser.md +384 -0
  147. package/reference/scaffold/recipes/customize-organization-model.md +281 -118
  148. package/reference/scaffold/recipes/extend-a-base-entity.md +8 -8
  149. package/reference/scaffold/recipes/extend-crm.md +40 -39
  150. package/reference/scaffold/recipes/extend-lead-gen.md +400 -401
  151. package/reference/scaffold/recipes/gate-by-feature-or-admin.md +118 -114
  152. package/reference/scaffold/recipes/index.md +47 -46
  153. package/reference/scaffold/recipes/query-the-knowledge-graph.md +227 -0
  154. package/reference/scaffold/reference/contracts.md +2389 -2121
  155. package/reference/scaffold/reference/feature-registry.md +9 -20
  156. package/reference/scaffold/reference/glossary.md +76 -76
  157. package/reference/scaffold/ui/composition-extensibility.mdx +233 -233
  158. package/reference/scaffold/ui/customization.md +243 -243
  159. package/reference/scaffold/ui/feature-flags-and-gating.md +46 -46
  160. package/reference/scaffold/ui/feature-shell.mdx +72 -72
  161. package/reference/scaffold/ui/recipes.md +221 -213
  162. package/reference/spine/spine-primer.md +96 -96
  163. package/reference/templates/index.mdx +47 -47
  164. package/reference/troubleshooting.mdx +223 -223
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevasis/sdk",
3
- "version": "1.20.2",
3
+ "version": "1.22.0",
4
4
  "description": "SDK for building Elevasis organization resources",
5
5
  "type": "module",
6
6
  "bin": {
@@ -57,9 +57,9 @@
57
57
  "tsup": "^8.0.0",
58
58
  "typescript": "5.9.2",
59
59
  "zod": "^4.1.0",
60
- "@repo/core": "0.22.0",
60
+ "@repo/eslint-config": "0.0.0",
61
61
  "@repo/typescript-config": "0.0.0",
62
- "@repo/eslint-config": "0.0.0"
62
+ "@repo/core": "0.24.0"
63
63
  },
64
64
  "scripts": {
65
65
  "lint": "eslint src --max-warnings 0",
@@ -1,3 +1,5 @@
1
+ <!-- @generated by scripts/monorepo/generate-reference-artifacts.js -- DO NOT EDIT -->
2
+
1
3
  # SDK Reference Navigation
2
4
 
3
5
  Auto-generated from the package reference manifests.
@@ -20,7 +22,7 @@ Package entries indexed: 55.
20
22
 
21
23
  | Resource | Location | Description | When to Load |
22
24
  | --- | --- | --- | --- |
23
- | Knowledge | `packages/core/src/knowledge/README.md` | Published knowledge query layer: byFeature/byKind/byOwner/governs/governedBy queries, parsePath, and output formatters. | (not specified) |
25
+ | Knowledge | `packages/core/src/knowledge/README.md` | Published knowledge query layer: bySystem/byKind/byOwner/governs/governedBy queries, parsePath, and output formatters. | (not specified) |
24
26
 
25
27
  ## @elevasis/core / Organization Model
26
28
 
@@ -153,17 +155,17 @@ Universal scaffold documentation for all SDK projects. Source locations are co-l
153
155
  | Document | Bundle Path | Description |
154
156
  | --- | --- | --- |
155
157
  | Scaffold Index | `scaffold/index.mdx` | Discovery entry point and navigation map |
156
- | Add a Feature | `scaffold/recipes/add-a-feature.md` | End-to-end feature addition recipe |
158
+ | Add a System | `scaffold/recipes/add-a-feature.md` | End-to-end system addition recipe |
157
159
  | Add a Resource | `scaffold/recipes/add-a-resource.md` | Workflow/agent authoring recipe |
158
- | Gate by Feature/Admin | `scaffold/recipes/gate-by-feature-or-admin.md` | Access control patterns |
160
+ | Gate by System/Admin | `scaffold/recipes/gate-by-feature-or-admin.md` | Access control patterns |
159
161
  | UI Recipes | `scaffold/ui/recipes.md` | Copy-paste UI recipes |
160
- | Feature Flags & Gating | `scaffold/ui/feature-flags-and-gating.md` | Three-concept gating model |
161
- | Customizing Features | `scaffold/ui/customization.md` | Sidebar composition patterns |
162
- | Feature Shell | `scaffold/ui/feature-shell.mdx` | FeatureModule manifest and provider |
162
+ | System Flags & Gating | `scaffold/ui/feature-flags-and-gating.md` | Three-concept gating model |
163
+ | Customizing Systems | `scaffold/ui/customization.md` | Sidebar composition patterns |
164
+ | System Shell | `scaffold/ui/feature-shell.mdx` | SystemModule manifest and provider |
163
165
  | Composition & Extensibility | `scaffold/ui/composition-extensibility.mdx` | Layout primitives and overrides |
164
166
  | Organization Model | `scaffold/core/organization-model.mdx` | Semantic contract and schema |
165
167
  | Organization Graph | `scaffold/core/organization-graph.mdx` | Graph derivation and Cytoscape |
166
168
  | Workflow Recipes | `scaffold/operations/workflow-recipes.md` | Workflow anatomy and adapters |
167
169
  | Glossary | `scaffold/reference/glossary.md` | Term definitions |
168
170
  | Contracts | `scaffold/reference/contracts.md` | Auto-generated type contracts |
169
- | Feature Registry | `scaffold/reference/feature-registry.md` | Auto-generated feature catalog |
171
+ | System Registry | `scaffold/reference/feature-registry.md` | Auto-generated system catalog |
@@ -35,7 +35,7 @@
35
35
  "subpath": "./knowledge",
36
36
  "kind": "subpath",
37
37
  "title": "Knowledge",
38
- "description": "Published knowledge query layer: byFeature/byKind/byOwner/governs/governedBy queries, parsePath, and output formatters.",
38
+ "description": "Published knowledge query layer: bySystem/byKind/byOwner/governs/governedBy queries, parsePath, and output formatters.",
39
39
  "group": "Knowledge",
40
40
  "order": 1,
41
41
  "sourcePath": "packages/core/src/knowledge/index.ts",
@@ -1,98 +1,98 @@
1
- #!/usr/bin/env node
2
- // post-edit-validate.mjs
3
- // PostToolUse hook — auto-formats with prettier, type-checks .ts/.tsx files.
4
- // Fires after Edit|Write|MultiEdit succeeds.
5
-
6
- import { existsSync, readFileSync } from 'node:fs'
7
- import { resolve, normalize, extname, join, dirname, relative } from 'node:path'
8
- import { execSync } from 'node:child_process'
9
-
10
- const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
11
-
12
- // Extensions prettier should format
13
- const PRETTIER_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.css', '.md'])
14
-
15
- // Extensions that trigger type-checking
16
- const TS_EXTENSIONS = new Set(['.ts', '.tsx'])
17
-
18
- function findNearestTsconfig(startDir) {
19
- let dir = startDir
20
- const root = normalize(ROOT)
21
- while (dir.length >= root.length) {
22
- const candidate = join(dir, 'tsconfig.json')
23
- if (existsSync(candidate)) return candidate
24
- const parent = dirname(dir)
25
- if (parent === dir) break
26
- dir = parent
27
- }
28
- return null
29
- }
30
-
31
- try {
32
- const chunks = []
33
- for await (const chunk of process.stdin) chunks.push(chunk)
34
- const input = JSON.parse(Buffer.concat(chunks).toString())
35
-
36
- const filePath = input.tool_input?.file_path
37
- if (!filePath) process.exit(0)
38
-
39
- const ext = extname(filePath).toLowerCase()
40
- const absPath = normalize(resolve(filePath))
41
- if (!existsSync(absPath)) process.exit(0)
42
-
43
- const results = []
44
-
45
- // 1. Prettier (skip silently if not installed yet, e.g. before first pnpm install)
46
- if (PRETTIER_EXTENSIONS.has(ext)) {
47
- try {
48
- execSync('pnpm exec prettier --version', { cwd: ROOT, stdio: 'pipe', timeout: 5_000 })
49
- try {
50
- execSync('pnpm exec prettier --write "' + absPath + '"', {
51
- cwd: ROOT,
52
- stdio: ['pipe', 'pipe', 'pipe'],
53
- timeout: 10_000
54
- })
55
- } catch (err) {
56
- const stderr = err.stderr?.toString().trim() || ''
57
- if (stderr && !/ignored/i.test(stderr)) {
58
- results.push('Prettier error: ' + stderr.slice(0, 300))
59
- }
60
- }
61
- } catch {
62
- // prettier not installed yet -- skip silently
63
- }
64
- }
65
-
66
- // 2. Type-check for .ts/.tsx
67
- if (TS_EXTENSIONS.has(ext)) {
68
- const tsconfig = findNearestTsconfig(dirname(absPath))
69
- if (tsconfig) {
70
- try {
71
- execSync('pnpm exec tsc --noEmit -p "' + tsconfig + '"', {
72
- cwd: ROOT,
73
- stdio: ['pipe', 'pipe', 'pipe'],
74
- timeout: 30_000,
75
- env: { ...process.env, NODE_OPTIONS: '--max-old-space-size=4096' }
76
- })
77
- } catch (err) {
78
- if (err.killed) process.exit(0) // Don't block on timeout
79
- const stdout = err.stdout?.toString() || ''
80
- if (stdout.includes('error TS')) {
81
- const errorLines = stdout
82
- .split('\n')
83
- .filter((l) => l.includes('error TS'))
84
- .slice(0, 10)
85
- results.push('Type errors after editing ' + filePath + ':\n' + errorLines.join('\n'))
86
- }
87
- }
88
- }
89
- }
90
-
91
- // Output errors to Claude's context (silence = success)
92
- if (results.length > 0) {
93
- process.stderr.write(results.join('\n\n'))
94
- process.exit(2) // Exit 2 = send stderr as feedback to Claude
95
- }
96
- } catch {}
97
-
98
- process.exit(0)
1
+ #!/usr/bin/env node
2
+ // post-edit-validate.mjs
3
+ // PostToolUse hook — auto-formats with prettier, type-checks .ts/.tsx files.
4
+ // Fires after Edit|Write|MultiEdit succeeds.
5
+
6
+ import { existsSync, readFileSync } from 'node:fs'
7
+ import { resolve, normalize, extname, join, dirname, relative } from 'node:path'
8
+ import { execSync } from 'node:child_process'
9
+
10
+ const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
11
+
12
+ // Extensions prettier should format
13
+ const PRETTIER_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs', '.json', '.css', '.md'])
14
+
15
+ // Extensions that trigger type-checking
16
+ const TS_EXTENSIONS = new Set(['.ts', '.tsx'])
17
+
18
+ function findNearestTsconfig(startDir) {
19
+ let dir = startDir
20
+ const root = normalize(ROOT)
21
+ while (dir.length >= root.length) {
22
+ const candidate = join(dir, 'tsconfig.json')
23
+ if (existsSync(candidate)) return candidate
24
+ const parent = dirname(dir)
25
+ if (parent === dir) break
26
+ dir = parent
27
+ }
28
+ return null
29
+ }
30
+
31
+ try {
32
+ const chunks = []
33
+ for await (const chunk of process.stdin) chunks.push(chunk)
34
+ const input = JSON.parse(Buffer.concat(chunks).toString())
35
+
36
+ const filePath = input.tool_input?.file_path
37
+ if (!filePath) process.exit(0)
38
+
39
+ const ext = extname(filePath).toLowerCase()
40
+ const absPath = normalize(resolve(filePath))
41
+ if (!existsSync(absPath)) process.exit(0)
42
+
43
+ const results = []
44
+
45
+ // 1. Prettier (skip silently if not installed yet, e.g. before first pnpm install)
46
+ if (PRETTIER_EXTENSIONS.has(ext)) {
47
+ try {
48
+ execSync('pnpm exec prettier --version', { cwd: ROOT, stdio: 'pipe', timeout: 5_000 })
49
+ try {
50
+ execSync('pnpm exec prettier --write "' + absPath + '"', {
51
+ cwd: ROOT,
52
+ stdio: ['pipe', 'pipe', 'pipe'],
53
+ timeout: 10_000
54
+ })
55
+ } catch (err) {
56
+ const stderr = err.stderr?.toString().trim() || ''
57
+ if (stderr && !/ignored/i.test(stderr)) {
58
+ results.push('Prettier error: ' + stderr.slice(0, 300))
59
+ }
60
+ }
61
+ } catch {
62
+ // prettier not installed yet -- skip silently
63
+ }
64
+ }
65
+
66
+ // 2. Type-check for .ts/.tsx
67
+ if (TS_EXTENSIONS.has(ext)) {
68
+ const tsconfig = findNearestTsconfig(dirname(absPath))
69
+ if (tsconfig) {
70
+ try {
71
+ execSync('pnpm exec tsc --noEmit -p "' + tsconfig + '"', {
72
+ cwd: ROOT,
73
+ stdio: ['pipe', 'pipe', 'pipe'],
74
+ timeout: 30_000,
75
+ env: { ...process.env, NODE_OPTIONS: '--max-old-space-size=4096' }
76
+ })
77
+ } catch (err) {
78
+ if (err.killed) process.exit(0) // Don't block on timeout
79
+ const stdout = err.stdout?.toString() || ''
80
+ if (stdout.includes('error TS')) {
81
+ const errorLines = stdout
82
+ .split('\n')
83
+ .filter((l) => l.includes('error TS'))
84
+ .slice(0, 10)
85
+ results.push('Type errors after editing ' + filePath + ':\n' + errorLines.join('\n'))
86
+ }
87
+ }
88
+ }
89
+ }
90
+
91
+ // Output errors to Claude's context (silence = success)
92
+ if (results.length > 0) {
93
+ process.stderr.write(results.join('\n\n'))
94
+ process.exit(2) // Exit 2 = send stderr as feedback to Claude
95
+ }
96
+ } catch {}
97
+
98
+ process.exit(0)
@@ -1,188 +1,188 @@
1
- #!/usr/bin/env node
2
- // scaffold-registry-reminder.mjs
3
- // PostToolUse hook — reads the compiled scaffold registry and emits advisory
4
- // reminders when an edited file matches a registry source pattern.
5
- //
6
- // Template twin of the monorepo hook. Gracefully no-ops when the compiled
7
- // registry is absent (e.g. before SDK delivers scaffold-registry.compiled.json
8
- // to external projects — Step 7/SDK milestone).
9
- //
10
- // Exit 0 always (advisory hook — never blocks).
11
-
12
- import { readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'node:fs'
13
- import { join, normalize, relative } from 'node:path'
14
-
15
- const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
16
- const LOG_DIR = join(ROOT, '.claude', 'logs')
17
- const LOG_FILE = join(LOG_DIR, 'scaffold-registry-reminder.log')
18
- const STATE_FILE = join(LOG_DIR, 'scaffold-registry-reminder.state.json')
19
- const REGISTRY_FILE = join(ROOT, '.claude', 'scaffold-registry.compiled.json')
20
-
21
- const DEFAULT_COOLDOWN_MS = 300_000 // 5 minutes
22
-
23
- const GENERATED_DIR_SEGMENTS = ['_generated', '_gen']
24
- const GENERATED_CONTENT_MARKER = '@generated'
25
-
26
- function log(msg) {
27
- try {
28
- mkdirSync(LOG_DIR, { recursive: true })
29
- appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`)
30
- } catch {}
31
- }
32
-
33
- function pathMatchesPattern(filePath, pattern) {
34
- const normalizedFile = filePath.replace(/\\/g, '/')
35
- const normalizedPattern = pattern.replace(/\\/g, '/')
36
-
37
- if (normalizedFile === normalizedPattern) return true
38
-
39
- if (normalizedPattern.endsWith('/**') || normalizedPattern.endsWith('/*')) {
40
- const prefix = normalizedPattern.slice(0, normalizedPattern.lastIndexOf('/*'))
41
- return normalizedFile.startsWith(prefix + '/')
42
- }
43
-
44
- if (normalizedPattern.includes('*')) {
45
- const escaped = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '[^/]*')
46
- return new RegExp(`^${escaped}$`).test(normalizedFile)
47
- }
48
-
49
- return normalizedFile.startsWith(normalizedPattern + '/')
50
- }
51
-
52
- function loadState() {
53
- try {
54
- return JSON.parse(readFileSync(STATE_FILE, 'utf-8'))
55
- } catch {
56
- return {}
57
- }
58
- }
59
-
60
- function saveState(state) {
61
- try {
62
- mkdirSync(LOG_DIR, { recursive: true })
63
- writeFileSync(STATE_FILE, JSON.stringify(state, null, 2) + '\n', 'utf-8')
64
- } catch {}
65
- }
66
-
67
- function throttleKey(entryId, filePath) {
68
- return `${entryId}:${filePath}`
69
- }
70
-
71
- function isCoolingDown(state, key, cooldownMs) {
72
- const last = state[key]
73
- if (!last) return false
74
- return Date.now() - last < cooldownMs
75
- }
76
-
77
- function formatDependentLine(dep) {
78
- const regen = dep.regen === 'manual' ? 'manual check' : dep.regen
79
- const hint = dep.hint ? ` [${dep.hint}]` : ''
80
- return ` - ${dep.path} -> ${regen}${hint}`
81
- }
82
-
83
- function emitReminder(entry, relFilePath) {
84
- const lines = [
85
- `\uD83D\uDD14 Scaffold reminder -- ${entry.id} (${relFilePath})`,
86
- ` Downstream scaffolds that may need updating:`
87
- ]
88
- for (const dep of entry.dependents) {
89
- lines.push(formatDependentLine(dep))
90
- }
91
- lines.push(
92
- ` If this is a scaffold-sensitive pattern not in the registry, also add an entry to .claude/scaffold-registry.yml.`
93
- )
94
- return lines.join('\n')
95
- }
96
-
97
- function emitMissingEntryHint(relFilePath) {
98
- return [
99
- `\uD83D\uDD14 Scaffold reminder -- unregistered generated path (${relFilePath})`,
100
- ` This path looks scaffold-generated but has no registry entry.`,
101
- ` If it is scaffold-sensitive, add a new entry to .claude/scaffold-registry.yml`,
102
- ` so the reminder hook and /work handoff can track it.`
103
- ].join('\n')
104
- }
105
-
106
- function looksLikeGeneratedPath(filePath) {
107
- const normalizedFile = filePath.replace(/\\/g, '/')
108
- const segments = normalizedFile.split('/')
109
- return segments.some((seg) => GENERATED_DIR_SEGMENTS.includes(seg))
110
- }
111
-
112
- function looksLikeGeneratedContent(absFilePath) {
113
- try {
114
- const content = readFileSync(absFilePath, 'utf-8').slice(0, 500)
115
- return content.includes(GENERATED_CONTENT_MARKER)
116
- } catch {
117
- return false
118
- }
119
- }
120
-
121
- try {
122
- const chunks = []
123
- for await (const chunk of process.stdin) chunks.push(chunk)
124
- const input = JSON.parse(Buffer.concat(chunks).toString())
125
-
126
- const rawFilePath = input.tool_input?.file_path
127
- if (!rawFilePath) process.exit(0)
128
-
129
- const absFilePath = normalize(rawFilePath)
130
- const relFilePath = relative(ROOT, absFilePath).replace(/\\/g, '/')
131
-
132
- // Graceful no-op when registry is absent (pre-SDK-delivery state)
133
- let registry
134
- try {
135
- const raw = readFileSync(REGISTRY_FILE, 'utf-8')
136
- registry = JSON.parse(raw)
137
- } catch {
138
- log(`SKIP — registry not found (pre-SDK-delivery)`)
139
- process.exit(0)
140
- }
141
-
142
- const entries = registry?.entries ?? []
143
-
144
- const matched = entries.filter((entry) =>
145
- (entry.sources ?? []).some((pattern) => pathMatchesPattern(relFilePath, pattern))
146
- )
147
-
148
- const state = loadState()
149
- const now = Date.now()
150
- const messages = []
151
-
152
- if (matched.length > 0) {
153
- for (const entry of matched) {
154
- const key = throttleKey(entry.id, relFilePath)
155
- const cooldown = entry.cooldown_ms ?? DEFAULT_COOLDOWN_MS
156
- if (isCoolingDown(state, key, cooldown)) {
157
- log(`THROTTLED — ${entry.id} for ${relFilePath}`)
158
- continue
159
- }
160
- messages.push(emitReminder(entry, relFilePath))
161
- state[key] = now
162
- log(`EMITTED — ${entry.id} for ${relFilePath}`)
163
- }
164
- } else {
165
- const isGenerated = looksLikeGeneratedPath(relFilePath) || looksLikeGeneratedContent(absFilePath)
166
-
167
- if (isGenerated) {
168
- const key = throttleKey('__missing__', relFilePath)
169
- if (!isCoolingDown(state, key, DEFAULT_COOLDOWN_MS)) {
170
- messages.push(emitMissingEntryHint(relFilePath))
171
- state[key] = now
172
- log(`EMITTED missing-entry hint for ${relFilePath}`)
173
- } else {
174
- log(`THROTTLED missing-entry hint for ${relFilePath}`)
175
- }
176
- }
177
- }
178
-
179
- if (messages.length > 0) {
180
- saveState(state)
181
- process.stderr.write(messages.join('\n\n') + '\n')
182
- process.exit(2)
183
- }
184
- } catch (err) {
185
- log(`ERROR: ${err.message}`)
186
- }
187
-
188
- process.exit(0)
1
+ #!/usr/bin/env node
2
+ // scaffold-registry-reminder.mjs
3
+ // PostToolUse hook — reads the compiled scaffold registry and emits advisory
4
+ // reminders when an edited file matches a registry source pattern.
5
+ //
6
+ // Template twin of the monorepo hook. Gracefully no-ops when the compiled
7
+ // registry is absent (e.g. before SDK delivers scaffold-registry.compiled.json
8
+ // to external projects — Step 7/SDK milestone).
9
+ //
10
+ // Exit 0 always (advisory hook — never blocks).
11
+
12
+ import { readFileSync, writeFileSync, mkdirSync, appendFileSync } from 'node:fs'
13
+ import { join, normalize, relative } from 'node:path'
14
+
15
+ const ROOT = process.env.CLAUDE_PROJECT_DIR ?? process.cwd()
16
+ const LOG_DIR = join(ROOT, '.claude', 'logs')
17
+ const LOG_FILE = join(LOG_DIR, 'scaffold-registry-reminder.log')
18
+ const STATE_FILE = join(LOG_DIR, 'scaffold-registry-reminder.state.json')
19
+ const REGISTRY_FILE = join(ROOT, '.claude', 'scaffold-registry.compiled.json')
20
+
21
+ const DEFAULT_COOLDOWN_MS = 300_000 // 5 minutes
22
+
23
+ const GENERATED_DIR_SEGMENTS = ['_generated', '_gen']
24
+ const GENERATED_CONTENT_MARKER = '@generated'
25
+
26
+ function log(msg) {
27
+ try {
28
+ mkdirSync(LOG_DIR, { recursive: true })
29
+ appendFileSync(LOG_FILE, `[${new Date().toISOString()}] ${msg}\n`)
30
+ } catch {}
31
+ }
32
+
33
+ function pathMatchesPattern(filePath, pattern) {
34
+ const normalizedFile = filePath.replace(/\\/g, '/')
35
+ const normalizedPattern = pattern.replace(/\\/g, '/')
36
+
37
+ if (normalizedFile === normalizedPattern) return true
38
+
39
+ if (normalizedPattern.endsWith('/**') || normalizedPattern.endsWith('/*')) {
40
+ const prefix = normalizedPattern.slice(0, normalizedPattern.lastIndexOf('/*'))
41
+ return normalizedFile.startsWith(prefix + '/')
42
+ }
43
+
44
+ if (normalizedPattern.includes('*')) {
45
+ const escaped = normalizedPattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '[^/]*')
46
+ return new RegExp(`^${escaped}$`).test(normalizedFile)
47
+ }
48
+
49
+ return normalizedFile.startsWith(normalizedPattern + '/')
50
+ }
51
+
52
+ function loadState() {
53
+ try {
54
+ return JSON.parse(readFileSync(STATE_FILE, 'utf-8'))
55
+ } catch {
56
+ return {}
57
+ }
58
+ }
59
+
60
+ function saveState(state) {
61
+ try {
62
+ mkdirSync(LOG_DIR, { recursive: true })
63
+ writeFileSync(STATE_FILE, JSON.stringify(state, null, 2) + '\n', 'utf-8')
64
+ } catch {}
65
+ }
66
+
67
+ function throttleKey(entryId, filePath) {
68
+ return `${entryId}:${filePath}`
69
+ }
70
+
71
+ function isCoolingDown(state, key, cooldownMs) {
72
+ const last = state[key]
73
+ if (!last) return false
74
+ return Date.now() - last < cooldownMs
75
+ }
76
+
77
+ function formatDependentLine(dep) {
78
+ const regen = dep.regen === 'manual' ? 'manual check' : dep.regen
79
+ const hint = dep.hint ? ` [${dep.hint}]` : ''
80
+ return ` - ${dep.path} -> ${regen}${hint}`
81
+ }
82
+
83
+ function emitReminder(entry, relFilePath) {
84
+ const lines = [
85
+ `\uD83D\uDD14 Scaffold reminder -- ${entry.id} (${relFilePath})`,
86
+ ` Downstream scaffolds that may need updating:`
87
+ ]
88
+ for (const dep of entry.dependents) {
89
+ lines.push(formatDependentLine(dep))
90
+ }
91
+ lines.push(
92
+ ` If this is a scaffold-sensitive pattern not in the registry, also add an entry to .claude/scaffold-registry.yml.`
93
+ )
94
+ return lines.join('\n')
95
+ }
96
+
97
+ function emitMissingEntryHint(relFilePath) {
98
+ return [
99
+ `\uD83D\uDD14 Scaffold reminder -- unregistered generated path (${relFilePath})`,
100
+ ` This path looks scaffold-generated but has no registry entry.`,
101
+ ` If it is scaffold-sensitive, add a new entry to .claude/scaffold-registry.yml`,
102
+ ` so the reminder hook and /work handoff can track it.`
103
+ ].join('\n')
104
+ }
105
+
106
+ function looksLikeGeneratedPath(filePath) {
107
+ const normalizedFile = filePath.replace(/\\/g, '/')
108
+ const segments = normalizedFile.split('/')
109
+ return segments.some((seg) => GENERATED_DIR_SEGMENTS.includes(seg))
110
+ }
111
+
112
+ function looksLikeGeneratedContent(absFilePath) {
113
+ try {
114
+ const content = readFileSync(absFilePath, 'utf-8').slice(0, 500)
115
+ return content.includes(GENERATED_CONTENT_MARKER)
116
+ } catch {
117
+ return false
118
+ }
119
+ }
120
+
121
+ try {
122
+ const chunks = []
123
+ for await (const chunk of process.stdin) chunks.push(chunk)
124
+ const input = JSON.parse(Buffer.concat(chunks).toString())
125
+
126
+ const rawFilePath = input.tool_input?.file_path
127
+ if (!rawFilePath) process.exit(0)
128
+
129
+ const absFilePath = normalize(rawFilePath)
130
+ const relFilePath = relative(ROOT, absFilePath).replace(/\\/g, '/')
131
+
132
+ // Graceful no-op when registry is absent (pre-SDK-delivery state)
133
+ let registry
134
+ try {
135
+ const raw = readFileSync(REGISTRY_FILE, 'utf-8')
136
+ registry = JSON.parse(raw)
137
+ } catch {
138
+ log(`SKIP — registry not found (pre-SDK-delivery)`)
139
+ process.exit(0)
140
+ }
141
+
142
+ const entries = registry?.entries ?? []
143
+
144
+ const matched = entries.filter((entry) =>
145
+ (entry.sources ?? []).some((pattern) => pathMatchesPattern(relFilePath, pattern))
146
+ )
147
+
148
+ const state = loadState()
149
+ const now = Date.now()
150
+ const messages = []
151
+
152
+ if (matched.length > 0) {
153
+ for (const entry of matched) {
154
+ const key = throttleKey(entry.id, relFilePath)
155
+ const cooldown = entry.cooldown_ms ?? DEFAULT_COOLDOWN_MS
156
+ if (isCoolingDown(state, key, cooldown)) {
157
+ log(`THROTTLED — ${entry.id} for ${relFilePath}`)
158
+ continue
159
+ }
160
+ messages.push(emitReminder(entry, relFilePath))
161
+ state[key] = now
162
+ log(`EMITTED — ${entry.id} for ${relFilePath}`)
163
+ }
164
+ } else {
165
+ const isGenerated = looksLikeGeneratedPath(relFilePath) || looksLikeGeneratedContent(absFilePath)
166
+
167
+ if (isGenerated) {
168
+ const key = throttleKey('__missing__', relFilePath)
169
+ if (!isCoolingDown(state, key, DEFAULT_COOLDOWN_MS)) {
170
+ messages.push(emitMissingEntryHint(relFilePath))
171
+ state[key] = now
172
+ log(`EMITTED missing-entry hint for ${relFilePath}`)
173
+ } else {
174
+ log(`THROTTLED missing-entry hint for ${relFilePath}`)
175
+ }
176
+ }
177
+ }
178
+
179
+ if (messages.length > 0) {
180
+ saveState(state)
181
+ process.stderr.write(messages.join('\n\n') + '\n')
182
+ process.exit(2)
183
+ }
184
+ } catch (err) {
185
+ log(`ERROR: ${err.message}`)
186
+ }
187
+
188
+ process.exit(0)