chati-dev 4.2.2 → 4.3.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 (221) hide show
  1. package/README.md +80 -53
  2. package/bin/chati.js +170 -5
  3. package/framework/agents/build/dev.md +509 -76
  4. package/framework/agents/deploy/devops.md +40 -25
  5. package/framework/agents/discover/brief.md +158 -22
  6. package/framework/agents/discover/brownfield-wu.md +26 -14
  7. package/framework/agents/discover/greenfield-wu.md +102 -14
  8. package/framework/agents/plan/architect-data-engineer.md +6 -6
  9. package/framework/agents/plan/architect-system.md +46 -12
  10. package/framework/agents/plan/architect.md +40 -20
  11. package/framework/agents/plan/detail.md +38 -24
  12. package/framework/agents/plan/phases.md +36 -26
  13. package/framework/agents/plan/tasks.md +114 -33
  14. package/framework/agents/plan/ux-brand-architect.md +240 -8
  15. package/framework/agents/plan/ux-component-engineer.md +28 -13
  16. package/framework/agents/plan/ux-researcher.md +7 -6
  17. package/framework/agents/plan/ux.md +55 -22
  18. package/framework/agents/quality/qa-implementation.md +143 -74
  19. package/framework/agents/quality/qa-planning.md +115 -42
  20. package/framework/agents/quality/qa-visual.md +448 -0
  21. package/framework/agents/shared/visualizer.md +128 -0
  22. package/framework/config.yaml +7 -6
  23. package/framework/constitution.md +127 -44
  24. package/framework/context/governance.md +12 -7
  25. package/framework/context/quality.md +6 -5
  26. package/framework/context/root.md +6 -6
  27. package/framework/data/entity-registry.yaml +377 -4
  28. package/framework/data/model-limits.json +19 -0
  29. package/framework/domains/agents/qa-visual.yaml +74 -0
  30. package/framework/domains/constitution.yaml +46 -2
  31. package/framework/domains/workflows/greenfield-fullstack.yaml +2 -2
  32. package/framework/hooks/advance-trigger.js +131 -0
  33. package/framework/hooks/brief-validator.js +83 -0
  34. package/framework/hooks/constitution-guard.js +24 -5
  35. package/framework/hooks/license-guard.js +62 -27
  36. package/framework/hooks/mode-governance.js +13 -2
  37. package/framework/hooks/model-governance.js +1 -0
  38. package/framework/hooks/post-dev.js +207 -0
  39. package/framework/hooks/prism-engine.js +274 -105
  40. package/framework/hooks/reasoning-escalator.js +371 -0
  41. package/framework/hooks/reference-trigger.js +117 -0
  42. package/framework/hooks/session-digest.js +50 -1
  43. package/framework/hooks/settings.json +32 -1
  44. package/framework/hooks/style-guard.js +25 -6
  45. package/framework/hooks/team-quality-gate.js +19 -12
  46. package/framework/hooks/undercover-guard.js +4 -2
  47. package/framework/i18n/en.yaml +3 -3
  48. package/framework/i18n/es.yaml +3 -3
  49. package/framework/i18n/fr.yaml +3 -3
  50. package/framework/i18n/pt.yaml +3 -3
  51. package/framework/intelligence/context-engine.md +4 -5
  52. package/framework/intelligence/decision-engine.md +1 -1
  53. package/framework/intelligence/hooks-performance.md +3 -3
  54. package/framework/migrations/v1.0-to-v1.1.yaml +1 -1
  55. package/framework/migrations/v1.4-to-v2.0.yaml +11 -11
  56. package/framework/migrations/v4.0-to-v4.1.yaml +2 -2
  57. package/framework/migrations/v4.2-to-v4.3.yaml +29 -0
  58. package/framework/orchestrator/chati-router.js +441 -0
  59. package/framework/orchestrator/chati-update.md +40 -40
  60. package/framework/orchestrator/chati.md +295 -94
  61. package/framework/rules/human-writing-style.md +47 -0
  62. package/framework/scaffold/motion-premium/README.md +99 -0
  63. package/framework/scaffold/motion-premium/app/globals.css.template +400 -0
  64. package/framework/scaffold/motion-premium/app/layout.tsx.template +110 -0
  65. package/framework/scaffold/motion-premium/components/animation/BackgroundCrossfadeProvider.tsx.template +170 -0
  66. package/framework/scaffold/motion-premium/components/animation/LenisProvider.tsx.template +49 -0
  67. package/framework/scaffold/motion-premium/components/animation/PageTransitionWrapper.tsx.template +83 -0
  68. package/framework/scaffold/motion-premium/components/animation/Preloader.tsx.template +171 -0
  69. package/framework/scaffold/motion-premium/components/ui/Container.tsx.template +69 -0
  70. package/framework/scaffold/motion-premium/components/ui/PageSection.tsx.template +74 -0
  71. package/framework/scaffold/motion-premium/lib/animations/gsap.ts.template +112 -0
  72. package/framework/scaffold/motion-premium/lib/animations/refreshCoordinator.ts.template +75 -0
  73. package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +119 -0
  74. package/framework/scaffold/motion-premium/lib/animations/useGsapContext.ts.template +92 -0
  75. package/framework/scaffold/motion-premium/lib/animations/useScrollSnapStepper.ts.template +265 -0
  76. package/framework/scaffold/motion-premium/lib/animations/useSmoothScroll.ts.template +67 -0
  77. package/framework/scaffold/motion-premium/lib/brand.ts.template +43 -0
  78. package/framework/scaffold/motion-premium/scaffold.yaml +178 -0
  79. package/framework/scaffold/motion-premium-3d/README.md +90 -0
  80. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoCanvas.tsx.template +81 -0
  81. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoClient.tsx.template +75 -0
  82. package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/page.tsx.template +26 -0
  83. package/framework/scaffold/motion-premium-3d/components/3d/CameraRig.tsx.template +100 -0
  84. package/framework/scaffold/motion-premium-3d/components/3d/CanvasProvider.tsx.template +85 -0
  85. package/framework/scaffold/motion-premium-3d/components/3d/InvalidateOnScroll.tsx.template +51 -0
  86. package/framework/scaffold/motion-premium-3d/components/3d/MeshCrossfade.tsx.template +79 -0
  87. package/framework/scaffold/motion-premium-3d/components/3d/ScrollCrossfade.tsx.template +88 -0
  88. package/framework/scaffold/motion-premium-3d/components/3d/ScrollScene.tsx.template +121 -0
  89. package/framework/scaffold/motion-premium-3d/components/webgl/SceneFallback.tsx.template +65 -0
  90. package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +68 -0
  91. package/framework/scaffold/motion-premium-3d/lib/webgl/detect.ts.template +69 -0
  92. package/framework/scaffold/motion-premium-3d/scaffold.yaml +136 -0
  93. package/framework/schemas/session.schema.json +109 -21
  94. package/framework/scripts/reference-capture.js +430 -0
  95. package/framework/scripts/visual-qa.js +686 -0
  96. package/framework/tasks/orchestrator-handoff.md +1 -1
  97. package/framework/tasks/orchestrator-resume.md +1 -1
  98. package/framework/tasks/orchestrator-route.md +1 -1
  99. package/framework/tasks/orchestrator-status.md +3 -3
  100. package/framework/tasks/qa-planning-gate-define.md +1 -1
  101. package/framework/templates/brandbook-html-tmpl.md +2 -2
  102. package/framework/templates/brandbook-tmpl.yaml +1 -1
  103. package/framework/templates/component-spec-tmpl.yaml +1 -1
  104. package/framework/templates/design-token-tmpl.yaml +1 -1
  105. package/framework/templates/icon-system-tmpl.yaml +1 -1
  106. package/framework/templates/team-planning-tasks.yaml +6 -5
  107. package/framework/workflows/brownfield-discovery.yaml +2 -2
  108. package/framework/workflows/brownfield-fullstack.yaml +15 -11
  109. package/framework/workflows/brownfield-service.yaml +14 -10
  110. package/framework/workflows/brownfield-ui.yaml +15 -11
  111. package/framework/workflows/greenfield-fullstack.yaml +16 -13
  112. package/framework/workflows/quick-flow.yaml +3 -3
  113. package/framework/workflows/standard-flow.yaml +12 -9
  114. package/package.json +12 -6
  115. package/src/autonomy/autonomous-gate.js +1 -0
  116. package/src/autonomy/build-state.js +1 -2
  117. package/src/autonomy/progress-reporter.js +1 -1
  118. package/src/config/agent-customizer.js +11 -3
  119. package/src/config/claude-settings-generator.js +27 -7
  120. package/src/config/context-file-generator.js +41 -21
  121. package/src/config/framework-adapter.js +1 -0
  122. package/src/config/gemini-hooks-generator.js +19 -7
  123. package/src/config/mcp-configs.js +1 -0
  124. package/src/context/layers/l1-global.js +2 -1
  125. package/src/dashboard/data-reader.js +4 -3
  126. package/src/dashboard/layout.js +2 -1
  127. package/src/decision/analyzer.js +6 -30
  128. package/src/decision/engine.js +4 -28
  129. package/src/decision/registry-healer.js +3 -2
  130. package/src/decision/registry-updater.js +23 -14
  131. package/src/extensions/loader.js +2 -8
  132. package/src/gates/g1-planning-complete.js +2 -1
  133. package/src/gates/g2-qa-planning.js +2 -1
  134. package/src/gates/g3-implementation.js +2 -1
  135. package/src/gates/g4-qa-implementation.js +3 -2
  136. package/src/gates/g5-deploy-ready.js +2 -1
  137. package/src/health/engine.js +4 -3
  138. package/src/installer/core.js +431 -81
  139. package/src/installer/preflight.js +131 -0
  140. package/src/installer/provider-overlay.js +3 -3
  141. package/src/installer/scaffold-applier.js +358 -0
  142. package/src/installer/templates.js +46 -29
  143. package/src/installer/validator.js +17 -12
  144. package/src/intelligence/registry-manager.js +22 -29
  145. package/src/intelligence/timeline.js +11 -6
  146. package/src/license/commands.js +1 -1
  147. package/src/license/wait.js +102 -0
  148. package/src/memory/agent-memory.js +81 -0
  149. package/src/memory/dream.js +32 -1
  150. package/src/merger/replace-merger.js +28 -15
  151. package/src/orchestrator/agent-selector.js +2 -1
  152. package/src/orchestrator/cli.js +1880 -71
  153. package/src/orchestrator/doctor.js +270 -0
  154. package/src/orchestrator/handoff-engine.js +4 -3
  155. package/src/orchestrator/index.js +2 -0
  156. package/src/orchestrator/pipeline-manager.js +306 -15
  157. package/src/orchestrator/session-manager.js +339 -6
  158. package/src/tasks/handoff.js +3 -2
  159. package/src/telemetry/config.js +4 -3
  160. package/src/telemetry/schema.js +1 -0
  161. package/src/terminal/collector.js +3 -2
  162. package/src/terminal/index.js +1 -2
  163. package/src/terminal/isolation.js +52 -18
  164. package/src/terminal/prompt-builder.js +42 -25
  165. package/src/terminal/run-parallel.js +1 -1
  166. package/src/terminal/run-team.js +3 -3
  167. package/src/terminal/team-task-list.js +43 -4
  168. package/src/upgrade/backup.js +3 -2
  169. package/src/upgrade/checker.js +3 -2
  170. package/src/upgrade/migrator.js +65 -7
  171. package/src/upgrade/tracked-files-detector.js +86 -0
  172. package/src/upgrade/user-messages.js +94 -0
  173. package/src/utils/config-parser.js +2 -1
  174. package/src/utils/feature-flags.js +2 -1
  175. package/src/utils/flatten-entities.js +69 -0
  176. package/src/utils/framework-dir.js +16 -0
  177. package/src/utils/model-id.js +85 -0
  178. package/src/utils/provider-limits.js +84 -23
  179. package/src/utils/schema-validator.js +1 -1
  180. package/src/wizard/i18n.js +5 -4
  181. package/src/wizard/index.js +14 -0
  182. package/assets/logo - c/303/263pia.png +0 -0
  183. package/assets/logo.svg +0 -42
  184. package/assets/logo2.png +0 -0
  185. package/assets/social-preview.png +0 -0
  186. package/scripts/bundle-framework.js +0 -69
  187. package/scripts/changelog-generator.js +0 -222
  188. package/scripts/codebase-mapper.js +0 -728
  189. package/scripts/commit-message-generator.js +0 -167
  190. package/scripts/coverage-analyzer.js +0 -260
  191. package/scripts/dependency-analyzer.js +0 -280
  192. package/scripts/doctor/checks/agents.js +0 -77
  193. package/scripts/doctor/checks/constitution.js +0 -41
  194. package/scripts/doctor/checks/domain-alignment.js +0 -58
  195. package/scripts/doctor/checks/prism-layers.js +0 -84
  196. package/scripts/doctor/checks/registry.js +0 -55
  197. package/scripts/doctor/checks/schemas.js +0 -61
  198. package/scripts/doctor/fixes/reference-fix.js +0 -100
  199. package/scripts/doctor/fixes/registry-fix.js +0 -56
  200. package/scripts/doctor/index.js +0 -212
  201. package/scripts/framework-analyzer.js +0 -308
  202. package/scripts/generate-constitution-domain.js +0 -253
  203. package/scripts/generate-signing-key.js +0 -33
  204. package/scripts/health-check.js +0 -481
  205. package/scripts/ide-sync.js +0 -326
  206. package/scripts/performance-analyzer.js +0 -325
  207. package/scripts/plan-tracker.js +0 -278
  208. package/scripts/populate-entity-registry.js +0 -481
  209. package/scripts/pr-review.js +0 -317
  210. package/scripts/rollback-manager.js +0 -310
  211. package/scripts/semantic-lint.js +0 -328
  212. package/scripts/sign-manifest.js +0 -53
  213. package/scripts/stuck-detector.js +0 -343
  214. package/scripts/test-quality-assessment.js +0 -257
  215. package/scripts/validate-agents.js +0 -368
  216. package/scripts/validate-package.js +0 -505
  217. package/scripts/validate-tasks.js +0 -465
  218. package/src/autonomy/worktree-manager.js +0 -250
  219. package/src/intelligence/decision-engine.js +0 -256
  220. package/src/intelligence/document-sharder.js +0 -221
  221. package/src/intelligence/elicitation.js +0 -265
@@ -0,0 +1,270 @@
1
+ /**
2
+ * @fileoverview /chati doctor — silent boot-time self-diagnostic.
3
+ *
4
+ * Runs a battery of fast (< 200ms) filesystem + version checks every time
5
+ * /chati boots. Pass-silent, fail-loud: the intent is that the user never
6
+ * sees doctor output when everything is fine; they see a specific,
7
+ * actionable message ONLY when something is off.
8
+ *
9
+ * Six checks, two severities:
10
+ *
11
+ * id severity description
12
+ * --------- -------- -----------
13
+ * license BLOCK ~/.chati-dev/license.yaml status != VALID (fail-open if missing)
14
+ * scaffold BLOCK projectDir/.chati.dev/ directory exists
15
+ * session BLOCK .chati/session.yaml parses cleanly (or is absent)
16
+ * hooks WARN .claude/settings.json has expected hook registrations
17
+ * model WARN session.active_model is populated (hook fills on first fire)
18
+ * node BLOCK process version >= 22
19
+ *
20
+ * BLOCK failures short-circuit boot with a fix instruction. WARN failures
21
+ * surface as non-blocking notices.
22
+ *
23
+ * Each check is a PURE FUNCTION on (options) — takes everything it needs
24
+ * as args (no global state). runDoctor composes them and aggregates the
25
+ * verdict.
26
+ */
27
+
28
+ import { existsSync, readFileSync } from 'fs';
29
+ import { join } from 'path';
30
+ import { readLicenseStatus } from '../license/wait.js';
31
+
32
+ export const SEVERITY = Object.freeze({
33
+ BLOCK: 'BLOCK',
34
+ WARN: 'WARN',
35
+ });
36
+
37
+ /**
38
+ * @typedef {object} CheckResult
39
+ * @property {string} id
40
+ * @property {'BLOCK'|'WARN'} severity
41
+ * @property {'pass'|'fail'} status
42
+ * @property {string} message
43
+ * @property {string} [fix] Suggested fix when status=fail.
44
+ */
45
+
46
+ // ---------------------------------------------------------------------------
47
+ // Individual checks — pure functions.
48
+ // ---------------------------------------------------------------------------
49
+
50
+ /**
51
+ * License status. Fail-open: missing file = pass (matches license-guard.js).
52
+ * @param {{ home?: string }} opts
53
+ * @returns {CheckResult}
54
+ */
55
+ export function checkLicense({ home = process.env.HOME || '' } = {}) {
56
+ // F-A5-003: delegate to the canonical reader in license/wait.js so the
57
+ // doctor and the runtime-block path share a single source of truth. If
58
+ // the YAML shape changes, both fail/pass the same way.
59
+ const result = readLicenseStatus(home);
60
+ if (!result.present) {
61
+ return { id: 'license', severity: SEVERITY.BLOCK, status: 'pass', message: 'No license file (demo mode allowed — fail-open).' };
62
+ }
63
+ if (result.error) {
64
+ return {
65
+ id: 'license',
66
+ severity: SEVERITY.BLOCK,
67
+ status: 'fail',
68
+ message: `Cannot read license file: ${result.error}`,
69
+ fix: 'Restore ~/.chati-dev/license.yaml or run: npx chati-dev activate --key=YOUR-KEY',
70
+ };
71
+ }
72
+ const status = result.status || 'UNKNOWN';
73
+ if (status === 'VALID') {
74
+ return { id: 'license', severity: SEVERITY.BLOCK, status: 'pass', message: 'License: VALID.' };
75
+ }
76
+ return {
77
+ id: 'license',
78
+ severity: SEVERITY.BLOCK,
79
+ status: 'fail',
80
+ message: `License status: ${status}`,
81
+ fix: 'Paste in another terminal: npx chati-dev activate --key=YOUR-KEY',
82
+ };
83
+ }
84
+
85
+ /**
86
+ * Scaffold presence in the project. Required — without it, hooks and
87
+ * agent files are unreachable.
88
+ * @param {{ projectDir: string }} opts
89
+ * @returns {CheckResult}
90
+ */
91
+ export function checkScaffold({ projectDir }) {
92
+ const scaffoldDir = join(projectDir, '.chati.dev');
93
+ if (existsSync(scaffoldDir)) {
94
+ return { id: 'scaffold', severity: SEVERITY.BLOCK, status: 'pass', message: '.chati.dev/ scaffold present.' };
95
+ }
96
+ return {
97
+ id: 'scaffold',
98
+ severity: SEVERITY.BLOCK,
99
+ status: 'fail',
100
+ message: '.chati.dev/ scaffold directory not found in project root.',
101
+ fix: 'Run: npx chati-dev init (from the project root)',
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Session file integrity. Absence is fine (pre-init state). Presence
107
+ * with parse error is a BLOCK — we cannot route agent messages.
108
+ * @param {{ projectDir: string }} opts
109
+ * @returns {CheckResult}
110
+ */
111
+ export function checkSession({ projectDir }) {
112
+ const sessionPath = join(projectDir, '.chati', 'session.yaml');
113
+ if (!existsSync(sessionPath)) {
114
+ return {
115
+ id: 'session', severity: SEVERITY.BLOCK, status: 'pass',
116
+ message: 'Session not yet initialized (pre-init or fresh project).',
117
+ };
118
+ }
119
+ try {
120
+ const raw = readFileSync(sessionPath, 'utf-8');
121
+ // Minimal integrity: must contain at least one YAML-ish `key:` line and
122
+ // no obviously malformed constructs.
123
+ const hasKey = /^\s*\w+:/m.test(raw);
124
+ const hasBadStructure = /\[\s*\w+:\s*/.test(raw); // `[key:` is not valid YAML shorthand
125
+ if (!hasKey || hasBadStructure) {
126
+ return {
127
+ id: 'session',
128
+ severity: SEVERITY.BLOCK,
129
+ status: 'fail',
130
+ message: 'session.yaml is present but appears corrupted.',
131
+ fix: 'Inspect .chati/session.yaml manually, or re-run: npx chati-dev init',
132
+ };
133
+ }
134
+ return { id: 'session', severity: SEVERITY.BLOCK, status: 'pass', message: 'session.yaml parses cleanly.' };
135
+ } catch (err) {
136
+ return {
137
+ id: 'session',
138
+ severity: SEVERITY.BLOCK,
139
+ status: 'fail',
140
+ message: `Cannot read session.yaml: ${err.message}`,
141
+ fix: 'Check filesystem permissions on .chati/session.yaml',
142
+ };
143
+ }
144
+ }
145
+
146
+ /**
147
+ * Hook drift detection. Looks for a representative sample of expected
148
+ * hooks in .claude/settings.json. Missing = WARN (hook system is
149
+ * advisory; system still works).
150
+ * @param {{ projectDir: string }} opts
151
+ * @returns {CheckResult}
152
+ */
153
+ export function checkHooks({ projectDir }) {
154
+ const settingsPath = join(projectDir, '.claude', 'settings.json');
155
+ if (!existsSync(settingsPath)) {
156
+ return {
157
+ id: 'hooks', severity: SEVERITY.WARN, status: 'pass',
158
+ message: '.claude/settings.json not found — skipping hook drift check.',
159
+ };
160
+ }
161
+ try {
162
+ const raw = readFileSync(settingsPath, 'utf-8');
163
+ const settings = JSON.parse(raw);
164
+ const hookConfigs = settings && settings.hooks ? settings.hooks : {};
165
+ const expected = ['prism-engine.js', 'license-guard.js'];
166
+ const flat = JSON.stringify(hookConfigs);
167
+ const missing = expected.filter(h => !flat.includes(h));
168
+ if (missing.length === 0) {
169
+ return { id: 'hooks', severity: SEVERITY.WARN, status: 'pass', message: 'Expected hooks registered.' };
170
+ }
171
+ return {
172
+ id: 'hooks',
173
+ severity: SEVERITY.WARN,
174
+ status: 'fail',
175
+ message: `Missing hook registrations: ${missing.join(', ')}`,
176
+ fix: 'Re-run installer to refresh .claude/settings.json: npx chati-dev init (is idempotent)',
177
+ };
178
+ } catch (err) {
179
+ return {
180
+ id: 'hooks',
181
+ severity: SEVERITY.WARN,
182
+ status: 'fail',
183
+ message: `Cannot parse .claude/settings.json: ${err.message}`,
184
+ };
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Active model detection. Populated by /chati boot or by prism-engine
190
+ * on first fire. Absent = WARN — context bracket will still compute via
191
+ * provider default, just less precise.
192
+ * @param {{ projectDir: string }} opts
193
+ * @returns {CheckResult}
194
+ */
195
+ export function checkModel({ projectDir }) {
196
+ const sessionPath = join(projectDir, '.chati', 'session.yaml');
197
+ if (!existsSync(sessionPath)) {
198
+ return {
199
+ id: 'model', severity: SEVERITY.WARN, status: 'pass',
200
+ message: 'Session not yet initialized — model detection deferred.',
201
+ };
202
+ }
203
+ try {
204
+ const raw = readFileSync(sessionPath, 'utf-8');
205
+ const match = raw.match(/^\s*active_model:\s*(.+)$/m);
206
+ const value = match ? match[1].trim().replace(/^["']|["']$/g, '') : '';
207
+ if (!value || value === 'null' || value === '~' || value === '') {
208
+ return {
209
+ id: 'model',
210
+ severity: SEVERITY.WARN,
211
+ status: 'fail',
212
+ message: 'active_model not set — hook will populate on next prompt.',
213
+ };
214
+ }
215
+ return { id: 'model', severity: SEVERITY.WARN, status: 'pass', message: `active_model: ${value}` };
216
+ } catch {
217
+ return {
218
+ id: 'model', severity: SEVERITY.WARN, status: 'pass',
219
+ message: 'Cannot read active_model; deferring to hook.',
220
+ };
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Node version gate. Framework assumes Node 22+ for modern ESM, fetch,
226
+ * globbing. Earlier versions break test runs and hook spawns.
227
+ * @param {{ versionString?: string }} opts
228
+ * @returns {CheckResult}
229
+ */
230
+ export function checkNode({ versionString = process.version } = {}) {
231
+ const match = versionString.match(/^v?(\d+)/);
232
+ const major = match ? parseInt(match[1], 10) : 0;
233
+ if (major >= 22) {
234
+ return { id: 'node', severity: SEVERITY.BLOCK, status: 'pass', message: `Node ${versionString} OK.` };
235
+ }
236
+ return {
237
+ id: 'node',
238
+ severity: SEVERITY.BLOCK,
239
+ status: 'fail',
240
+ message: `Node ${versionString} is below the required v22.`,
241
+ fix: 'Install Node 22+: https://nodejs.org/en/download',
242
+ };
243
+ }
244
+
245
+ // ---------------------------------------------------------------------------
246
+ // runDoctor — aggregate all six checks.
247
+ // ---------------------------------------------------------------------------
248
+
249
+ /**
250
+ * @param {{ projectDir: string, home?: string }} opts
251
+ * @returns {{
252
+ * ok: boolean,
253
+ * checks: CheckResult[],
254
+ * blockers: CheckResult[],
255
+ * warnings: CheckResult[]
256
+ * }}
257
+ */
258
+ export function runDoctor(opts) {
259
+ const checks = [
260
+ checkLicense({ home: opts.home }),
261
+ checkScaffold({ projectDir: opts.projectDir }),
262
+ checkSession({ projectDir: opts.projectDir }),
263
+ checkHooks({ projectDir: opts.projectDir }),
264
+ checkModel({ projectDir: opts.projectDir }),
265
+ checkNode(),
266
+ ];
267
+ const blockers = checks.filter(c => c.status === 'fail' && c.severity === SEVERITY.BLOCK);
268
+ const warnings = checks.filter(c => c.status === 'fail' && c.severity === SEVERITY.WARN);
269
+ return { ok: blockers.length === 0, checks, blockers, warnings };
270
+ }
@@ -12,6 +12,7 @@ import { updateClaudeMd } from '../memory/magic-docs.js';
12
12
  import { generateContextFiles } from '../config/context-file-generator.js';
13
13
  import { existsSync, readdirSync } from 'fs';
14
14
  import { join } from 'path';
15
+ import { resolveFrameworkDir } from '../utils/framework-dir.js';
15
16
 
16
17
  /**
17
18
  * Execute a full agent-to-agent handoff.
@@ -151,11 +152,11 @@ export function validateHandoffPreconditions(params, projectDir = '.') {
151
152
  // Deterministic artifact checks for UX agent handoffs
152
153
  const agent = params.fromTask?.agent || params.fromTask?.id?.split('-')[0] || '';
153
154
  if (agent === 'ux') {
154
- const brandbookPath = join(projectDir, 'chati.dev', 'artifacts', '4-UX', 'brandbook.html');
155
+ const brandbookPath = join(projectDir, resolveFrameworkDir(projectDir), 'artifacts', '4-UX', 'brandbook.html');
155
156
  if (!existsSync(brandbookPath)) {
156
157
  issues.push('brandbook.html is missing — UX handoff requires the visual brandbook file on disk');
157
158
  }
158
- const discoveryLogPath = join(projectDir, 'chati.dev', 'artifacts', '4-UX', 'component-discovery-log.md');
159
+ const discoveryLogPath = join(projectDir, resolveFrameworkDir(projectDir), 'artifacts', '4-UX', 'component-discovery-log.md');
159
160
  if (!existsSync(discoveryLogPath)) {
160
161
  issues.push('component-discovery-log.md is missing — UX handoff requires component discovery documentation');
161
162
  }
@@ -225,7 +226,7 @@ export function loadHandoffContext(projectDir, fromAgent) {
225
226
  * @returns {object[]} Array of handoff summaries
226
227
  */
227
228
  export function getHandoffHistory(projectDir) {
228
- const handoffsDir = join(projectDir, 'chati.dev', 'artifacts', 'handoffs');
229
+ const handoffsDir = join(projectDir, resolveFrameworkDir(projectDir), 'artifacts', 'handoffs');
229
230
 
230
231
  if (!existsSync(handoffsDir)) {
231
232
  return [];
@@ -60,6 +60,8 @@ export {
60
60
  updateSession,
61
61
  recordModeTransition,
62
62
  recordAgentCompletion,
63
+ parseHandoffDecisionTrail,
64
+ parseHandoffScaffoldSignals,
63
65
  getSessionSummary,
64
66
  validateSession,
65
67
  claimSession,