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
@@ -6,36 +6,49 @@ import yaml from 'js-yaml';
6
6
  export function generateSessionYaml(config) {
7
7
  const { projectName, projectType, language, selectedIDEs, selectedMCPs, llmProvider, allProviders } = config;
8
8
 
9
+ // Schema MUST match session-manager.js DEFAULT_SESSION fields,
10
+ // otherwise validateSession() rejects installer-generated sessions.
9
11
  const session = {
10
12
  schema_version: '1.0',
13
+ version: '1.0',
14
+ mode: 'discover',
11
15
  project: {
12
16
  name: projectName,
13
17
  type: projectType,
14
18
  state: 'discover',
15
19
  },
16
- execution_mode: 'interactive',
17
- execution_profile: 'guided',
18
- current_agent: '',
19
20
  language: language,
21
+ project_type: projectType,
22
+ started_at: new Date().toISOString(),
23
+ current_agent: '',
24
+ pipeline_position: 0,
25
+ execution_mode: 'interactive',
26
+ user_level: 'auto',
27
+ user_level_confidence: 0.0,
20
28
  ides: selectedIDEs,
21
29
  mcps: selectedMCPs,
22
30
  providers_enabled: allProviders && allProviders.length > 0 ? allProviders : [llmProvider || 'claude'],
23
31
  primary_provider: llmProvider || 'claude',
24
32
  active_provider: llmProvider || 'claude',
25
- user_level: 'auto',
26
- user_level_confidence: 0.0,
33
+ completed_agents: [],
34
+ agent_results: {},
35
+ mode_transitions: [],
36
+ deviations: [],
27
37
  agents: {},
28
38
  backlog: [],
29
39
  last_handoff: '',
30
- deviations: [],
31
- profile_transitions: [],
40
+ // Article XXIII — Reasoning Tier Governance.
41
+ // Tier starts at 'standard' and is promoted by reasoning-escalator.js
42
+ // in response to friction signals. See chati.dev/hooks/reasoning-escalator.js.
43
+ reasoning_tier: 'standard',
44
+ reasoning_escalations: [],
32
45
  };
33
46
 
34
- // Initialize all 12 agent statuses
47
+ // Initialize all 13 agent statuses
35
48
  const agentNames = [
36
49
  'greenfield-wu', 'brownfield-wu', 'brief', 'detail',
37
50
  'architect', 'ux', 'phases', 'tasks',
38
- 'qa-planning', 'dev', 'qa-implementation', 'devops',
51
+ 'qa-planning', 'dev', 'qa-implementation', 'qa-visual', 'devops',
39
52
  ];
40
53
 
41
54
  for (const agent of agentNames) {
@@ -62,7 +75,7 @@ export const PROVIDER_MODEL_MAPS = {
62
75
  orchestrator: 'sonnet', 'greenfield-wu': 'haiku', 'brownfield-wu': 'opus',
63
76
  brief: 'sonnet', detail: 'opus', architect: 'opus', ux: 'sonnet',
64
77
  phases: 'sonnet', tasks: 'sonnet', 'qa-planning': 'opus',
65
- dev: 'opus', 'qa-implementation': 'opus', devops: 'sonnet',
78
+ dev: 'opus', 'qa-implementation': 'opus', 'qa-visual': 'opus', devops: 'sonnet',
66
79
  },
67
80
  },
68
81
  gemini: {
@@ -71,7 +84,7 @@ export const PROVIDER_MODEL_MAPS = {
71
84
  orchestrator: 'flash', 'greenfield-wu': 'flash', 'brownfield-wu': 'pro',
72
85
  brief: 'flash', detail: 'pro', architect: 'pro', ux: 'flash',
73
86
  phases: 'flash', tasks: 'flash', 'qa-planning': 'pro',
74
- dev: 'pro', 'qa-implementation': 'pro', devops: 'flash',
87
+ dev: 'pro', 'qa-implementation': 'pro', 'qa-visual': 'pro', devops: 'flash',
75
88
  },
76
89
  },
77
90
  codex: {
@@ -80,7 +93,7 @@ export const PROVIDER_MODEL_MAPS = {
80
93
  orchestrator: 'mini', 'greenfield-wu': 'mini', 'brownfield-wu': 'codex',
81
94
  brief: 'mini', detail: 'codex', architect: 'codex', ux: 'mini',
82
95
  phases: 'mini', tasks: 'mini', 'qa-planning': 'codex',
83
- dev: 'codex', 'qa-implementation': 'codex', devops: 'mini',
96
+ dev: 'codex', 'qa-implementation': 'codex', 'qa-visual': 'codex', devops: 'mini',
84
97
  },
85
98
  },
86
99
  };
@@ -89,7 +102,8 @@ export const PROVIDER_MODEL_MAPS = {
89
102
  * Generate config.yaml content
90
103
  */
91
104
  export function generateConfigYaml(config) {
92
- const { version, projectType, language, selectedIDEs, llmProvider, allProviders } = config;
105
+ const { version, projectType, language, llmProvider, allProviders } = config;
106
+ const selectedIDEs = config.selectedIDEs || [];
93
107
  const provider = llmProvider || 'claude';
94
108
  const providers = allProviders && allProviders.length > 0 ? allProviders : [provider];
95
109
 
@@ -140,7 +154,7 @@ export function generateConfigYaml(config) {
140
154
  // pipeline silently. See plan: was previously false for "safe rollout"
141
155
  // but the v4.2.0 launch never actually shipped, so this is a fresh start.
142
156
  agent_teams: true,
143
- team_planning_size: 3,
157
+ team_planning_size: 4,
144
158
  team_build_size: 2,
145
159
  team_echo_threshold: 0.92,
146
160
  team_correction_cycles_max: 2,
@@ -193,7 +207,7 @@ Type \`/chati\` to start.
193
207
  * Also reads AGENTS.md for project context (auto-generated by context-file-generator).
194
208
  */
195
209
  export function generateCodexSkill(options = {}) {
196
- const orchPath = options.orchestratorPath || 'chati.dev/orchestrator/chati.md';
210
+ const orchPath = options.orchestratorPath || '.chati.dev/orchestrator/chati.md';
197
211
  return `---
198
212
  name: chati
199
213
  description: >
@@ -231,8 +245,8 @@ Read and execute the full orchestrator at \`${orchPath}\`.
231
245
  **Context to pass:**
232
246
  - \`.chati/session.yaml\` (session state — includes language)
233
247
  - \`AGENTS.md\` (project context)
234
- - \`chati.dev/artifacts/handoffs/\` (latest handoff)
235
- - \`chati.dev/config.yaml\` (version info)
248
+ - \`artifacts/handoffs/\` (latest handoff)
249
+ - \`.chati.dev/config.yaml\` (version info)
236
250
 
237
251
  **User input:** $ARGUMENTS
238
252
  `;
@@ -240,12 +254,9 @@ Read and execute the full orchestrator at \`${orchPath}\`.
240
254
 
241
255
  /**
242
256
  * Generate Gemini CLI TOML command (.gemini/commands/chati.toml)
243
- *
244
- * Gemini CLI custom commands use TOML format with `description` and `prompt` fields.
245
- * Supports {{args}} for user arguments and @{file} for file injection.
246
257
  */
247
258
  export function generateGeminiRouter(options = {}) {
248
- const orchPath = options.orchestratorPath || 'chati.dev/orchestrator/chati.md';
259
+ const orchPath = options.orchestratorPath || '.chati.dev/orchestrator/chati.md';
249
260
  return `description = "Activate Chati.dev orchestrator"
250
261
  prompt = """
251
262
  CRITICAL — Language Override:
@@ -262,8 +273,8 @@ NEVER create or reference CLAUDE.md, CLAUDE.local.md, or .claude/ directories.
262
273
  Context to load:
263
274
  - .chati/session.yaml (session state — includes language)
264
275
  - GEMINI.md (project context)
265
- - chati.dev/artifacts/handoffs/ (latest handoff)
266
- - chati.dev/config.yaml (version info)
276
+ - artifacts/handoffs/ (latest handoff)
277
+ - .chati.dev/config.yaml (version info)
267
278
 
268
279
  User input: {{args}}
269
280
  """
@@ -276,15 +287,17 @@ User input: {{args}}
276
287
  export function generateClaudeLocalMd() {
277
288
  return `# Chati.dev Runtime State
278
289
 
290
+ <!-- chati-lock:start -->
279
291
  ## Session Lock
280
292
  **Status: INACTIVE** — Type \`/chati\` to activate.
293
+ <!-- chati-lock:end -->
281
294
 
282
- <!-- SESSION-LOCK:INACTIVE -->
283
-
295
+ <!-- chati-state:start -->
284
296
  ## Current State
285
297
  - **Agent**: None (ready to start)
286
298
  - **Pipeline**: Pre-start
287
299
  - **Mode**: interactive
300
+ <!-- chati-state:end -->
288
301
 
289
302
  ## Recent Decisions
290
303
  _No decisions yet. Start with /chati._
@@ -301,15 +314,17 @@ _Auto-updated by Chati.dev orchestrator_
301
314
  export function generateGeminiSessionLock() {
302
315
  return `# Chati.dev Runtime State
303
316
 
317
+ <!-- chati-lock:start -->
304
318
  ## Session Lock
305
319
  **Status: INACTIVE** — Type \`/chati\` to activate.
320
+ <!-- chati-lock:end -->
306
321
 
307
- <!-- SESSION-LOCK:INACTIVE -->
308
-
322
+ <!-- chati-state:start -->
309
323
  ## Current State
310
324
  - **Agent**: None (ready to start)
311
325
  - **Pipeline**: Pre-start
312
326
  - **Mode**: interactive
327
+ <!-- chati-state:end -->
313
328
 
314
329
  ## Recent Decisions
315
330
  _No decisions yet. Start with /chati._
@@ -326,15 +341,17 @@ _Auto-updated by Chati.dev orchestrator_
326
341
  export function generateAgentsOverrideMd() {
327
342
  return `# Chati.dev Runtime State
328
343
 
344
+ <!-- chati-lock:start -->
329
345
  ## Session Lock
330
346
  **Status: INACTIVE** — Type \`$chati\` to activate.
347
+ <!-- chati-lock:end -->
331
348
 
332
- <!-- SESSION-LOCK:INACTIVE -->
333
-
349
+ <!-- chati-state:start -->
334
350
  ## Current State
335
351
  - **Agent**: None (ready to start)
336
352
  - **Pipeline**: Pre-start
337
353
  - **Mode**: interactive
354
+ <!-- chati-state:end -->
338
355
 
339
356
  ## Recent Decisions
340
357
  _No decisions yet. Start with $chati._
@@ -6,6 +6,11 @@ import { loadManifest } from './manifest.js';
6
6
  import { ADAPTABLE_FILES } from '../config/framework-adapter.js';
7
7
  import { validateSchema, CONFIG_SCHEMA } from '../utils/schema-validator.js';
8
8
 
9
+ function resolveFrameworkDir(targetDir) {
10
+ if (existsSync(join(targetDir, '.chati.dev'))) return '.chati.dev';
11
+ return 'chati.dev';
12
+ }
13
+
9
14
  /**
10
15
  * Validate chati.dev installation
11
16
  * Checks all primary agents, constitution, session, schemas, etc.
@@ -47,7 +52,7 @@ export async function validateInstallation(targetDir) {
47
52
 
48
53
  let agentCount = 0;
49
54
  for (const file of agentFiles) {
50
- const filePath = join(targetDir, 'chati.dev', file);
55
+ const filePath = join(targetDir, resolveFrameworkDir(targetDir), file);
51
56
  if (existsSync(filePath)) {
52
57
  const content = readFileSync(filePath, 'utf-8');
53
58
  // Check that agent implements protocols (look for key protocol references)
@@ -63,11 +68,11 @@ export async function validateInstallation(targetDir) {
63
68
  if (results.agents.pass) results.passed += 1;
64
69
 
65
70
  // Check constitution
66
- const constitutionPath = join(targetDir, 'chati.dev', 'constitution.md');
71
+ const constitutionPath = join(targetDir, resolveFrameworkDir(targetDir), 'constitution.md');
67
72
  if (existsSync(constitutionPath)) {
68
73
  const content = readFileSync(constitutionPath, 'utf-8');
69
74
  const articleCount = (content.match(/^## Article/gm) || []).length;
70
- results.constitution.pass = articleCount >= 19;
75
+ results.constitution.pass = articleCount >= 22;
71
76
  results.constitution.details.push({ articleCount });
72
77
  }
73
78
  results.total += 1;
@@ -86,7 +91,7 @@ export async function validateInstallation(targetDir) {
86
91
  ];
87
92
  let schemaCount = 0;
88
93
  for (const file of schemaFiles) {
89
- if (existsSync(join(targetDir, 'chati.dev', 'schemas', file))) {
94
+ if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'schemas', file))) {
90
95
  schemaCount++;
91
96
  }
92
97
  }
@@ -102,7 +107,7 @@ export async function validateInstallation(targetDir) {
102
107
  ];
103
108
  let workflowCount = 0;
104
109
  for (const file of workflowFiles) {
105
- if (existsSync(join(targetDir, 'chati.dev', 'workflows', file))) {
110
+ if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'workflows', file))) {
106
111
  workflowCount++;
107
112
  }
108
113
  }
@@ -118,7 +123,7 @@ export async function validateInstallation(targetDir) {
118
123
  ];
119
124
  let templateCount = 0;
120
125
  for (const file of templateFiles) {
121
- if (existsSync(join(targetDir, 'chati.dev', 'templates', file))) {
126
+ if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'templates', file))) {
122
127
  templateCount++;
123
128
  }
124
129
  }
@@ -137,7 +142,7 @@ export async function validateInstallation(targetDir) {
137
142
  ];
138
143
  let intelCount = 0;
139
144
  for (const file of intelligenceFiles) {
140
- if (existsSync(join(targetDir, 'chati.dev', file))) {
145
+ if (existsSync(join(targetDir, resolveFrameworkDir(targetDir), file))) {
141
146
  intelCount++;
142
147
  }
143
148
  }
@@ -147,7 +152,7 @@ export async function validateInstallation(targetDir) {
147
152
  if (results.intelligence.pass) results.passed += 1;
148
153
 
149
154
  // Check entity registry
150
- const registryPath = join(targetDir, 'chati.dev', 'data', 'entity-registry.yaml');
155
+ const registryPath = join(targetDir, resolveFrameworkDir(targetDir), 'data', 'entity-registry.yaml');
151
156
  results.registry.pass = existsSync(registryPath);
152
157
  results.total += 1;
153
158
  if (results.registry.pass) results.passed += 1;
@@ -164,7 +169,7 @@ export async function validateInstallation(targetDir) {
164
169
  let contextCount = 0;
165
170
  for (const file of contextFileNames) {
166
171
  // Check source (chati.dev/context/) OR deployed (.claude/rules/chati/)
167
- const inSource = existsSync(join(targetDir, 'chati.dev', 'context', file));
172
+ const inSource = existsSync(join(targetDir, resolveFrameworkDir(targetDir), 'context', file));
168
173
  const inRules = existsSync(join(targetDir, '.claude', 'rules', 'chati', file));
169
174
  if (inSource || inRules) {
170
175
  contextCount++;
@@ -176,7 +181,7 @@ export async function validateInstallation(targetDir) {
176
181
  if (results.context.pass) results.passed += 1;
177
182
 
178
183
  // Check provider overlays (if multiple providers configured)
179
- const configYamlPath = join(targetDir, 'chati.dev', 'config.yaml');
184
+ const configYamlPath = join(targetDir, resolveFrameworkDir(targetDir), 'config.yaml');
180
185
  if (existsSync(configYamlPath)) {
181
186
  try {
182
187
  const configContent = readFileSync(configYamlPath, 'utf-8');
@@ -208,7 +213,7 @@ export async function validateInstallation(targetDir) {
208
213
  const missingFiles = [];
209
214
 
210
215
  for (const file of ADAPTABLE_FILES) {
211
- const overlayPath = join(targetDir, 'chati.dev', '.adapted', provider, file);
216
+ const overlayPath = join(targetDir, resolveFrameworkDir(targetDir), '.adapted', provider, file);
212
217
  if (existsSync(overlayPath)) {
213
218
  present++;
214
219
  } else {
@@ -253,7 +258,7 @@ export async function validateInstallation(targetDir) {
253
258
  const mismatches = [];
254
259
 
255
260
  for (const [relPath, entry] of Object.entries(manifest.files)) {
256
- const absPath = join(targetDir, 'chati.dev', relPath);
261
+ const absPath = join(targetDir, resolveFrameworkDir(targetDir), relPath);
257
262
  if (existsSync(absPath)) {
258
263
  const currentHash = hashFile(absPath);
259
264
  if (currentHash === entry.hash) {
@@ -1,14 +1,25 @@
1
1
  import { existsSync, readFileSync } from 'fs';
2
2
  import { join } from 'path';
3
3
  import yaml from 'js-yaml';
4
+ import { resolveFrameworkDir } from '../utils/framework-dir.js';
5
+ import { flattenEntitiesMap } from '../utils/flatten-entities.js';
6
+
7
+ /**
8
+ * Get the entity registry path for a given project.
9
+ * Supports both .chati.dev/ and chati.dev/ structures.
10
+ * @param {string} targetDir
11
+ * @returns {string} Relative path from project root
12
+ */
13
+ function getRegistryPath(targetDir) {
14
+ return `${resolveFrameworkDir(targetDir)}/data/entity-registry.yaml`;
15
+ }
4
16
 
5
- const REGISTRY_PATH = 'chati.dev/data/entity-registry.yaml';
6
17
 
7
18
  /**
8
19
  * Check registry integrity against filesystem
9
20
  */
10
21
  export function checkRegistry(targetDir) {
11
- const registryPath = join(targetDir, REGISTRY_PATH);
22
+ const registryPath = join(targetDir, getRegistryPath(targetDir));
12
23
  if (!existsSync(registryPath)) {
13
24
  return { valid: false, error: 'Entity registry not found', missing: [], orphaned: [] };
14
25
  }
@@ -22,7 +33,7 @@ export function checkRegistry(targetDir) {
22
33
  const found = [];
23
34
 
24
35
  // Check each registered entity exists on disk
25
- const entities = flattenEntities(registry.entities);
36
+ const entities = flattenEntitiesMap(registry.entities);
26
37
  for (const entity of entities) {
27
38
  const filePath = join(targetDir, entity.path);
28
39
  if (existsSync(filePath)) {
@@ -44,7 +55,7 @@ export function checkRegistry(targetDir) {
44
55
  * Get registry statistics
45
56
  */
46
57
  export function getRegistryStats(targetDir) {
47
- const registryPath = join(targetDir, REGISTRY_PATH);
58
+ const registryPath = join(targetDir, getRegistryPath(targetDir));
48
59
  if (!existsSync(registryPath)) {
49
60
  return { exists: false, totalEntities: 0, byType: {} };
50
61
  }
@@ -54,7 +65,7 @@ export function getRegistryStats(targetDir) {
54
65
  return { exists: true, totalEntities: 0, byType: {}, error: 'Parse error' };
55
66
  }
56
67
 
57
- const entities = flattenEntities(registry.entities);
68
+ const entities = flattenEntitiesMap(registry.entities);
58
69
  const byType = {};
59
70
  for (const entity of entities) {
60
71
  const type = entity.type || 'unknown';
@@ -111,9 +122,10 @@ export function runHealthCheck(targetDir) {
111
122
  'session.schema.json', 'config.schema.json', 'task.schema.json',
112
123
  'context.schema.json', 'memory.schema.json',
113
124
  ];
125
+ const fwDir = resolveFrameworkDir(targetDir);
114
126
  let validSchemas = 0;
115
127
  for (const file of schemaFiles) {
116
- const schemaPath = join(targetDir, 'chati.dev', 'schemas', file);
128
+ const schemaPath = join(targetDir, fwDir, 'schemas', file);
117
129
  if (existsSync(schemaPath)) {
118
130
  try {
119
131
  JSON.parse(readFileSync(schemaPath, 'utf-8'));
@@ -127,12 +139,12 @@ export function runHealthCheck(targetDir) {
127
139
  checks.schemas.details = `${validSchemas}/${schemaFiles.length} valid`;
128
140
 
129
141
  // 3. Constitution check
130
- const constitutionPath = join(targetDir, 'chati.dev', 'constitution.md');
142
+ const constitutionPath = join(targetDir, fwDir, 'constitution.md');
131
143
  if (existsSync(constitutionPath)) {
132
144
  const content = readFileSync(constitutionPath, 'utf-8');
133
145
  const articleCount = (content.match(/^## Article/gm) || []).length;
134
- checks.constitution.pass = articleCount >= 19;
135
- checks.constitution.details = `${articleCount}/19 articles`;
146
+ checks.constitution.pass = articleCount >= 22;
147
+ checks.constitution.details = `${articleCount}/23 articles`;
136
148
  } else {
137
149
  checks.constitution.details = 'Not found';
138
150
  }
@@ -149,7 +161,7 @@ export function runHealthCheck(targetDir) {
149
161
  ];
150
162
  let foundAgents = 0;
151
163
  for (const p of agentPaths) {
152
- if (existsSync(join(targetDir, 'chati.dev', p))) foundAgents++;
164
+ if (existsSync(join(targetDir, fwDir, p))) foundAgents++;
153
165
  }
154
166
  checks.agents.pass = foundAgents === 13;
155
167
  checks.agents.details = `${foundAgents}/13 present`;
@@ -181,22 +193,3 @@ function loadRegistry(registryPath) {
181
193
  }
182
194
  }
183
195
 
184
- /**
185
- * Flatten nested entity categories into flat array
186
- */
187
- function flattenEntities(entities) {
188
- if (!entities) return [];
189
- const flat = [];
190
-
191
- for (const [, items] of Object.entries(entities)) {
192
- if (typeof items === 'object' && items !== null) {
193
- for (const [name, entity] of Object.entries(items)) {
194
- if (entity && entity.path) {
195
- flat.push({ name, ...entity });
196
- }
197
- }
198
- }
199
- }
200
-
201
- return flat;
202
- }
@@ -6,11 +6,6 @@
6
6
  * results, handoffs, and deviations.
7
7
  */
8
8
 
9
- /**
10
- * @deprecated Not currently imported by any production module.
11
- * Retained for potential future integration. Review for removal or integration by v5.0.
12
- */
13
-
14
9
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
15
10
  import { join, dirname } from 'path';
16
11
 
@@ -38,6 +33,8 @@ export const EventType = {
38
33
  PROVIDER_SELECTED: 'provider_selected',
39
34
  SESSION_STARTED: 'session_started',
40
35
  SESSION_ENDED: 'session_ended',
36
+ CORRECTION_TRIGGERED: 'correction_triggered',
37
+ CORRECTION_ESCALATED: 'correction_escalated',
41
38
  };
42
39
 
43
40
  const TIMELINE_FILE = '.chati/timeline.json';
@@ -82,8 +79,16 @@ function saveTimeline(projectDir, timeline) {
82
79
  */
83
80
  export function recordEvent(projectDir, type, agent, data = {}) {
84
81
  const timeline = loadTimeline(projectDir);
85
- _counter++;
86
82
 
83
+ // Dedup: skip if the last event of this type already targets the same agent.
84
+ // Prevents duplicate AGENT_ACTIVATED entries when handleAdvance chains into
85
+ // handleNext while the caller also invokes next separately.
86
+ const last = timeline[timeline.length - 1];
87
+ if (last && last.type === type && last.agent === agent) {
88
+ return last;
89
+ }
90
+
91
+ _counter++;
87
92
  const event = {
88
93
  id: `evt-${Date.now()}-${_counter}`,
89
94
  type,
@@ -37,7 +37,7 @@ export async function runActivate(projectDir, keyArg) {
37
37
  const plan = result.plan ?? 'trial';
38
38
  const days = result.days_remaining ?? '?';
39
39
  console.log(`\n✓ Activated! Plan: ${plan} — ${days} day(s) remaining.`);
40
- console.log(` Key saved to chati.dev/config.yaml`);
40
+ console.log(` Key saved to ~/.chati-dev/license.yaml`);
41
41
  console.log(` Run /chati to start.\n`);
42
42
  } catch (err) {
43
43
  console.error(`\n✗ Activation failed: ${err.message}`);
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @fileoverview Poll ~/.chati-dev/license.yaml until status=VALID.
3
+ *
4
+ * Part of the autonomous-completion workstream (ADR-AUTO-01): when a /chati
5
+ * session boots with no VALID license, the orchestrator tells the user to
6
+ * run `npx chati-dev activate --key=X` in another terminal, then calls
7
+ * this util to block until the file appears with VALID status.
8
+ *
9
+ * Design invariants:
10
+ * - Never echo the license key to chat (the whole reason we don't use
11
+ * chat-paste). We only check status.
12
+ * - Bounded wait: default 60s timeout, 3s poll interval.
13
+ * - Fast-fail on EXPIRED / INVALID / REVOKED — those need a NEW key,
14
+ * polling won't resolve them.
15
+ * - Pure function on time: cancellation via AbortSignal supported so
16
+ * callers (tests, interactive CLI) can stop the loop.
17
+ */
18
+
19
+ import { existsSync, readFileSync } from 'fs';
20
+ import { join } from 'path';
21
+
22
+ export const DEFAULT_POLL_INTERVAL_MS = 3_000;
23
+ export const DEFAULT_TIMEOUT_MS = 60_000;
24
+
25
+ const TERMINAL_BAD_STATUSES = new Set(['EXPIRED', 'INVALID', 'REVOKED']);
26
+
27
+ /**
28
+ * Read ~/.chati-dev/license.yaml and extract the status field.
29
+ * Pure + synchronous — safe to call from poll loop.
30
+ *
31
+ * @param {string} home
32
+ * @returns {{ present: boolean, status: string|null, reason: string|null, error?: string }}
33
+ */
34
+ export function readLicenseStatus(home) {
35
+ const licensePath = join(home, '.chati-dev', 'license.yaml');
36
+ if (!existsSync(licensePath)) {
37
+ return { present: false, status: null, reason: null };
38
+ }
39
+ try {
40
+ const raw = readFileSync(licensePath, 'utf-8');
41
+ const statusMatch = raw.match(/^\s*status:\s*["']?(\w+)/m);
42
+ const reasonMatch = raw.match(/^\s*reason:\s*["']?([^"'\n]+)/m);
43
+ return {
44
+ present: true,
45
+ status: statusMatch ? statusMatch[1].toUpperCase() : null,
46
+ reason: reasonMatch ? reasonMatch[1].trim() : null,
47
+ };
48
+ } catch (err) {
49
+ return { present: true, status: null, reason: null, error: err.message };
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Poll for a VALID license. Resolves when:
55
+ * - Status becomes VALID → { valid: true, status: 'VALID' }
56
+ * - Status is terminal-bad (EXPIRED|INVALID|REVOKED) → { valid: false, status, reason }
57
+ * - Timeout elapses → { valid: false, status: 'TIMEOUT', reason: 'No valid license detected within N seconds' }
58
+ * - Caller aborts → { valid: false, status: 'ABORTED' }
59
+ *
60
+ * @param {object} [opts]
61
+ * @param {string} [opts.home] Override HOME (default: process.env.HOME).
62
+ * @param {number} [opts.intervalMs] Poll period (default 3000).
63
+ * @param {number} [opts.timeoutMs] Total wait (default 60_000).
64
+ * @param {AbortSignal} [opts.signal] Cancel the loop externally.
65
+ * @param {(ms:number)=>Promise<void>} [opts.sleep] Injectable sleep (tests).
66
+ * @returns {Promise<{ valid: boolean, status: string|null, reason: string|null, waitedMs: number }>}
67
+ */
68
+ export async function waitForLicense(opts = {}) {
69
+ const home = opts.home || process.env.HOME || '';
70
+ const intervalMs = opts.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
71
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
72
+ const signal = opts.signal;
73
+ const sleep = opts.sleep || ((ms) => new Promise(r => setTimeout(r, ms)));
74
+
75
+ const startedAt = Date.now();
76
+
77
+ for (;;) {
78
+ if (signal?.aborted) {
79
+ return { valid: false, status: 'ABORTED', reason: 'Poll aborted by caller', waitedMs: Date.now() - startedAt };
80
+ }
81
+
82
+ const snap = readLicenseStatus(home);
83
+ if (snap.status === 'VALID') {
84
+ return { valid: true, status: 'VALID', reason: null, waitedMs: Date.now() - startedAt };
85
+ }
86
+ if (snap.status && TERMINAL_BAD_STATUSES.has(snap.status)) {
87
+ return { valid: false, status: snap.status, reason: snap.reason || `License ${snap.status}`, waitedMs: Date.now() - startedAt };
88
+ }
89
+
90
+ const elapsed = Date.now() - startedAt;
91
+ if (elapsed >= timeoutMs) {
92
+ return {
93
+ valid: false,
94
+ status: 'TIMEOUT',
95
+ reason: `No VALID license detected at ~/.chati-dev/license.yaml within ${Math.round(timeoutMs / 1000)}s.`,
96
+ waitedMs: elapsed,
97
+ };
98
+ }
99
+
100
+ await sleep(Math.min(intervalMs, timeoutMs - elapsed));
101
+ }
102
+ }
@@ -167,6 +167,87 @@ export function writeAgentMemory(projectDir, agentName, entry) {
167
167
  return { saved: true };
168
168
  }
169
169
 
170
+ /**
171
+ * Write a minimal MEMORY.md for an agent that has none, derived from a
172
+ * session digest (memory-bootstrap-v1, F-A5-006).
173
+ *
174
+ * Rules:
175
+ * - Only writes when the target MEMORY.md does not exist or is empty
176
+ * (idempotent; second call on the same agent is a no-op).
177
+ * - Entries are tagged `source: bootstrap` and confidence `low` so
178
+ * dream.js can safely re-classify / deduplicate on a later full run.
179
+ * - The produced file carries a `<!-- bootstrap-write: true -->` HTML
180
+ * comment header for auditability.
181
+ *
182
+ * @param {string} projectDir
183
+ * @param {string} agentName
184
+ * @param {object} digest - Session digest shape from session-digest.js
185
+ * @returns {{ written: boolean, reason?: string, path?: string }}
186
+ */
187
+ export function writeBootstrapMemory(projectDir, agentName, digest) {
188
+ if (!agentName || agentName === 'none') {
189
+ return { written: false, reason: 'no-agent' };
190
+ }
191
+ const memoryPath = getAgentMemoryPath(projectDir, agentName);
192
+
193
+ // Idempotency: skip if a non-empty MEMORY.md already exists.
194
+ if (existsSync(memoryPath)) {
195
+ try {
196
+ const existing = readFileSync(memoryPath, 'utf-8').trim();
197
+ if (existing.length > 0) {
198
+ return { written: false, reason: 'already-populated' };
199
+ }
200
+ } catch { /* fall through to write */ }
201
+ }
202
+
203
+ const dir = dirname(memoryPath);
204
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
205
+
206
+ const header = `<!-- bootstrap-write: true -->\n<!-- source: session-digest bootstrap (memory-bootstrap-v1) -->\n`;
207
+ const entries = [];
208
+
209
+ // Seed Episodic with the current pipeline context.
210
+ if (digest?.mode && digest?.pipelinePosition) {
211
+ entries.push({
212
+ category: 'Episodic',
213
+ content: `Resumed at mode=${digest.mode}, pipeline_position=${digest.pipelinePosition}, workflow=${digest.workflow || 'unknown'}.`,
214
+ confidence: 'low',
215
+ tags: ['source:bootstrap'],
216
+ });
217
+ }
218
+
219
+ // Seed Semantic from active_task (if digest has one).
220
+ if (digest?.active_task) {
221
+ entries.push({
222
+ category: 'Semantic',
223
+ content: `Active task on resume: ${String(digest.active_task).slice(0, 500)}`,
224
+ confidence: 'low',
225
+ tags: ['source:bootstrap'],
226
+ });
227
+ }
228
+
229
+ // Seed Procedural from gotchas_found (capped).
230
+ if (Array.isArray(digest?.gotchas_found) && digest.gotchas_found.length > 0) {
231
+ for (const g of digest.gotchas_found.slice(0, 5)) {
232
+ if (!g?.message) continue;
233
+ entries.push({
234
+ category: 'Procedural',
235
+ content: `Known gotcha [${g.severity || 'info'}] ${g.id || ''}: ${String(g.message).slice(0, 200)}`,
236
+ confidence: 'low',
237
+ tags: ['source:bootstrap', `severity:${g.severity || 'info'}`],
238
+ });
239
+ }
240
+ }
241
+
242
+ if (entries.length === 0) {
243
+ return { written: false, reason: 'no-content' };
244
+ }
245
+
246
+ const body = formatMemoryMarkdown(entries);
247
+ writeFileSync(memoryPath, header + '\n' + body, 'utf-8');
248
+ return { written: true, path: memoryPath };
249
+ }
250
+
170
251
  /**
171
252
  * Search across all agent memories.
172
253
  * @param {string} projectDir - Project directory