chati-dev 1.4.0 → 2.0.2

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 (208) hide show
  1. package/README.md +40 -24
  2. package/framework/agents/build/dev.md +343 -0
  3. package/framework/agents/clarity/architect.md +112 -0
  4. package/framework/agents/clarity/brief.md +182 -0
  5. package/framework/agents/clarity/brownfield-wu.md +181 -0
  6. package/framework/agents/clarity/detail.md +110 -0
  7. package/framework/agents/clarity/greenfield-wu.md +153 -0
  8. package/framework/agents/clarity/ux.md +112 -0
  9. package/framework/config.yaml +3 -3
  10. package/framework/constitution.md +31 -1
  11. package/framework/context/governance.md +37 -0
  12. package/framework/context/protocols.md +34 -0
  13. package/framework/context/quality.md +27 -0
  14. package/framework/context/root.md +24 -0
  15. package/framework/data/entity-registry.yaml +1 -1
  16. package/framework/domains/agents/architect.yaml +51 -0
  17. package/framework/domains/agents/brief.yaml +47 -0
  18. package/framework/domains/agents/brownfield-wu.yaml +49 -0
  19. package/framework/domains/agents/detail.yaml +47 -0
  20. package/framework/domains/agents/dev.yaml +49 -0
  21. package/framework/domains/agents/devops.yaml +43 -0
  22. package/framework/domains/agents/greenfield-wu.yaml +47 -0
  23. package/framework/domains/agents/orchestrator.yaml +49 -0
  24. package/framework/domains/agents/phases.yaml +47 -0
  25. package/framework/domains/agents/qa-implementation.yaml +43 -0
  26. package/framework/domains/agents/qa-planning.yaml +44 -0
  27. package/framework/domains/agents/tasks.yaml +48 -0
  28. package/framework/domains/agents/ux.yaml +50 -0
  29. package/framework/domains/constitution.yaml +77 -0
  30. package/framework/domains/global.yaml +64 -0
  31. package/framework/domains/workflows/brownfield-discovery.yaml +16 -0
  32. package/framework/domains/workflows/brownfield-fullstack.yaml +26 -0
  33. package/framework/domains/workflows/brownfield-service.yaml +22 -0
  34. package/framework/domains/workflows/brownfield-ui.yaml +22 -0
  35. package/framework/domains/workflows/greenfield-fullstack.yaml +26 -0
  36. package/framework/hooks/constitution-guard.js +101 -0
  37. package/framework/hooks/mode-governance.js +92 -0
  38. package/framework/hooks/model-governance.js +76 -0
  39. package/framework/hooks/prism-engine.js +89 -0
  40. package/framework/hooks/session-digest.js +60 -0
  41. package/framework/hooks/settings.json +44 -0
  42. package/framework/i18n/en.yaml +3 -3
  43. package/framework/i18n/es.yaml +3 -3
  44. package/framework/i18n/fr.yaml +3 -3
  45. package/framework/i18n/pt.yaml +3 -3
  46. package/framework/intelligence/decision-engine.md +1 -1
  47. package/framework/migrations/v1.4-to-v2.0.yaml +167 -0
  48. package/framework/migrations/v2.0-to-v2.0.1.yaml +132 -0
  49. package/framework/orchestrator/chati.md +284 -6
  50. package/framework/tasks/architect-api-design.md +63 -0
  51. package/framework/tasks/architect-consolidate.md +47 -0
  52. package/framework/tasks/architect-db-design.md +73 -0
  53. package/framework/tasks/architect-design.md +95 -0
  54. package/framework/tasks/architect-security-review.md +62 -0
  55. package/framework/tasks/architect-stack-selection.md +53 -0
  56. package/framework/tasks/brief-consolidate.md +249 -0
  57. package/framework/tasks/brief-constraint-identify.md +277 -0
  58. package/framework/tasks/brief-extract-requirements.md +339 -0
  59. package/framework/tasks/brief-stakeholder-map.md +176 -0
  60. package/framework/tasks/brief-validate-completeness.md +121 -0
  61. package/framework/tasks/brownfield-wu-architecture-map.md +394 -0
  62. package/framework/tasks/brownfield-wu-deep-discovery.md +312 -0
  63. package/framework/tasks/brownfield-wu-dependency-scan.md +359 -0
  64. package/framework/tasks/brownfield-wu-migration-plan.md +483 -0
  65. package/framework/tasks/brownfield-wu-report.md +325 -0
  66. package/framework/tasks/brownfield-wu-risk-assess.md +424 -0
  67. package/framework/tasks/detail-acceptance-criteria.md +372 -0
  68. package/framework/tasks/detail-consolidate.md +138 -0
  69. package/framework/tasks/detail-edge-case-analysis.md +300 -0
  70. package/framework/tasks/detail-expand-prd.md +389 -0
  71. package/framework/tasks/detail-nfr-extraction.md +223 -0
  72. package/framework/tasks/dev-code-review.md +404 -0
  73. package/framework/tasks/dev-consolidate.md +543 -0
  74. package/framework/tasks/dev-debug.md +322 -0
  75. package/framework/tasks/dev-implement.md +252 -0
  76. package/framework/tasks/dev-iterate.md +411 -0
  77. package/framework/tasks/dev-pr-prepare.md +497 -0
  78. package/framework/tasks/dev-refactor.md +342 -0
  79. package/framework/tasks/dev-test-write.md +306 -0
  80. package/framework/tasks/devops-ci-setup.md +412 -0
  81. package/framework/tasks/devops-consolidate.md +712 -0
  82. package/framework/tasks/devops-deploy-config.md +598 -0
  83. package/framework/tasks/devops-monitoring-setup.md +658 -0
  84. package/framework/tasks/devops-release-prepare.md +673 -0
  85. package/framework/tasks/greenfield-wu-analyze-empty.md +169 -0
  86. package/framework/tasks/greenfield-wu-report.md +266 -0
  87. package/framework/tasks/greenfield-wu-scaffold-detection.md +203 -0
  88. package/framework/tasks/greenfield-wu-tech-stack-assess.md +255 -0
  89. package/framework/tasks/orchestrator-deviation.md +260 -0
  90. package/framework/tasks/orchestrator-escalate.md +276 -0
  91. package/framework/tasks/orchestrator-handoff.md +243 -0
  92. package/framework/tasks/orchestrator-health.md +372 -0
  93. package/framework/tasks/orchestrator-mode-switch.md +262 -0
  94. package/framework/tasks/orchestrator-resume.md +189 -0
  95. package/framework/tasks/orchestrator-route.md +169 -0
  96. package/framework/tasks/orchestrator-spawn-terminal.md +358 -0
  97. package/framework/tasks/orchestrator-status.md +260 -0
  98. package/framework/tasks/orchestrator-suggest-mode.md +372 -0
  99. package/framework/tasks/phases-breakdown.md +91 -0
  100. package/framework/tasks/phases-dependency-mapping.md +67 -0
  101. package/framework/tasks/phases-mvp-scoping.md +94 -0
  102. package/framework/tasks/qa-impl-consolidate.md +522 -0
  103. package/framework/tasks/qa-impl-performance-test.md +487 -0
  104. package/framework/tasks/qa-impl-regression-check.md +413 -0
  105. package/framework/tasks/qa-impl-sast-scan.md +402 -0
  106. package/framework/tasks/qa-impl-test-execute.md +344 -0
  107. package/framework/tasks/qa-impl-verdict.md +339 -0
  108. package/framework/tasks/qa-planning-consolidate.md +309 -0
  109. package/framework/tasks/qa-planning-coverage-plan.md +338 -0
  110. package/framework/tasks/qa-planning-gate-define.md +339 -0
  111. package/framework/tasks/qa-planning-risk-matrix.md +631 -0
  112. package/framework/tasks/qa-planning-test-strategy.md +217 -0
  113. package/framework/tasks/tasks-acceptance-write.md +75 -0
  114. package/framework/tasks/tasks-consolidate.md +57 -0
  115. package/framework/tasks/tasks-decompose.md +80 -0
  116. package/framework/tasks/tasks-estimate.md +66 -0
  117. package/framework/tasks/ux-a11y-check.md +49 -0
  118. package/framework/tasks/ux-component-map.md +55 -0
  119. package/framework/tasks/ux-consolidate.md +46 -0
  120. package/framework/tasks/ux-user-flow.md +46 -0
  121. package/framework/tasks/ux-wireframe.md +76 -0
  122. package/package.json +2 -2
  123. package/scripts/bundle-framework.js +2 -0
  124. package/scripts/changelog-generator.js +222 -0
  125. package/scripts/codebase-mapper.js +728 -0
  126. package/scripts/commit-message-generator.js +167 -0
  127. package/scripts/coverage-analyzer.js +260 -0
  128. package/scripts/dependency-analyzer.js +280 -0
  129. package/scripts/framework-analyzer.js +308 -0
  130. package/scripts/generate-constitution-domain.js +253 -0
  131. package/scripts/health-check.js +481 -0
  132. package/scripts/ide-sync.js +327 -0
  133. package/scripts/performance-analyzer.js +325 -0
  134. package/scripts/plan-tracker.js +278 -0
  135. package/scripts/populate-entity-registry.js +481 -0
  136. package/scripts/pr-review.js +317 -0
  137. package/scripts/rollback-manager.js +310 -0
  138. package/scripts/stuck-detector.js +343 -0
  139. package/scripts/test-quality-assessment.js +257 -0
  140. package/scripts/validate-agents.js +367 -0
  141. package/scripts/validate-tasks.js +465 -0
  142. package/src/autonomy/autonomous-gate.js +293 -0
  143. package/src/autonomy/index.js +51 -0
  144. package/src/autonomy/mode-manager.js +225 -0
  145. package/src/autonomy/mode-suggester.js +283 -0
  146. package/src/autonomy/progress-reporter.js +268 -0
  147. package/src/autonomy/safety-net.js +320 -0
  148. package/src/context/bracket-tracker.js +79 -0
  149. package/src/context/domain-loader.js +107 -0
  150. package/src/context/engine.js +144 -0
  151. package/src/context/formatter.js +184 -0
  152. package/src/context/index.js +4 -0
  153. package/src/context/layers/l0-constitution.js +28 -0
  154. package/src/context/layers/l1-global.js +37 -0
  155. package/src/context/layers/l2-agent.js +39 -0
  156. package/src/context/layers/l3-workflow.js +42 -0
  157. package/src/context/layers/l4-task.js +24 -0
  158. package/src/decision/analyzer.js +167 -0
  159. package/src/decision/engine.js +270 -0
  160. package/src/decision/index.js +38 -0
  161. package/src/decision/registry-healer.js +450 -0
  162. package/src/decision/registry-updater.js +330 -0
  163. package/src/gates/circuit-breaker.js +119 -0
  164. package/src/gates/g1-planning-complete.js +153 -0
  165. package/src/gates/g2-qa-planning.js +153 -0
  166. package/src/gates/g3-implementation.js +188 -0
  167. package/src/gates/g4-qa-implementation.js +207 -0
  168. package/src/gates/g5-deploy-ready.js +180 -0
  169. package/src/gates/gate-base.js +144 -0
  170. package/src/gates/index.js +46 -0
  171. package/src/installer/brownfield-upgrader.js +249 -0
  172. package/src/installer/core.js +82 -11
  173. package/src/installer/file-hasher.js +51 -0
  174. package/src/installer/manifest.js +117 -0
  175. package/src/installer/templates.js +17 -15
  176. package/src/installer/transaction.js +229 -0
  177. package/src/installer/validator.js +18 -1
  178. package/src/intelligence/registry-manager.js +2 -2
  179. package/src/memory/agent-memory.js +255 -0
  180. package/src/memory/gotchas-injector.js +72 -0
  181. package/src/memory/gotchas.js +361 -0
  182. package/src/memory/index.js +35 -0
  183. package/src/memory/search.js +233 -0
  184. package/src/memory/session-digest.js +239 -0
  185. package/src/merger/env-merger.js +112 -0
  186. package/src/merger/index.js +56 -0
  187. package/src/merger/replace-merger.js +51 -0
  188. package/src/merger/yaml-merger.js +127 -0
  189. package/src/orchestrator/agent-selector.js +285 -0
  190. package/src/orchestrator/deviation-handler.js +350 -0
  191. package/src/orchestrator/handoff-engine.js +271 -0
  192. package/src/orchestrator/index.js +67 -0
  193. package/src/orchestrator/intent-classifier.js +264 -0
  194. package/src/orchestrator/pipeline-manager.js +492 -0
  195. package/src/orchestrator/pipeline-state.js +223 -0
  196. package/src/orchestrator/session-manager.js +409 -0
  197. package/src/tasks/executor.js +195 -0
  198. package/src/tasks/handoff.js +226 -0
  199. package/src/tasks/index.js +4 -0
  200. package/src/tasks/loader.js +210 -0
  201. package/src/tasks/router.js +182 -0
  202. package/src/terminal/collector.js +216 -0
  203. package/src/terminal/index.js +30 -0
  204. package/src/terminal/isolation.js +129 -0
  205. package/src/terminal/monitor.js +277 -0
  206. package/src/terminal/spawner.js +269 -0
  207. package/src/upgrade/checker.js +1 -1
  208. package/src/wizard/i18n.js +3 -3
@@ -0,0 +1,320 @@
1
+ /**
2
+ * @fileoverview Safety net for autonomous execution
3
+ * Auto-pauses pipeline on dangerous conditions
4
+ */
5
+
6
+ /**
7
+ * Safety conditions that trigger automatic pause.
8
+ */
9
+ export const SAFETY_TRIGGERS = {
10
+ CONSECUTIVE_FAILURES: 'consecutive_failures', // 3+ failures in a row
11
+ LOW_SCORE: 'low_score', // Score < 70%
12
+ CRITICAL_RISK: 'critical_risk', // Security/data risk detected
13
+ TIMEOUT: 'timeout', // Agent exceeded time limit
14
+ GOTCHA_SPIKE: 'gotcha_spike', // 5+ new gotchas in current session
15
+ };
16
+
17
+ // Critical risk keywords
18
+ const CRITICAL_RISK_KEYWORDS = [
19
+ 'security',
20
+ 'vulnerability',
21
+ 'exploit',
22
+ 'injection',
23
+ 'authentication',
24
+ 'authorization',
25
+ 'data loss',
26
+ 'data corruption',
27
+ 'production',
28
+ 'database drop',
29
+ 'rm -rf',
30
+ 'delete all',
31
+ ];
32
+
33
+ /**
34
+ * Check all safety conditions against current state.
35
+ * @param {object} state
36
+ * @param {number} state.consecutiveFailures - Number of consecutive gate failures
37
+ * @param {number} state.lastScore - Most recent gate score
38
+ * @param {string[]} state.riskFlags - Active risk flags
39
+ * @param {number} state.sessionGotchas - New gotchas this session
40
+ * @param {number} [state.agentDurationMs] - Time current agent has been running
41
+ * @param {number} [state.timeoutMs] - Maximum allowed duration
42
+ * @returns {{ safe: boolean, triggers: object[], recommendation: string }}
43
+ */
44
+ export function checkSafety(state) {
45
+ const triggers = [];
46
+
47
+ // Check each trigger
48
+ Object.values(SAFETY_TRIGGERS).forEach(trigger => {
49
+ const result = evaluateTrigger(trigger, state);
50
+ if (result.triggered) {
51
+ triggers.push(result);
52
+ }
53
+ });
54
+
55
+ const safe = triggers.length === 0;
56
+ const recommendation = safe
57
+ ? 'All safety checks passed'
58
+ : getRecommendedAction(triggers).reason;
59
+
60
+ return {
61
+ safe,
62
+ triggers,
63
+ recommendation,
64
+ };
65
+ }
66
+
67
+ /**
68
+ * Evaluate a single safety condition.
69
+ * @param {string} trigger - SAFETY_TRIGGERS value
70
+ * @param {object} state
71
+ * @returns {{ triggered: boolean, severity: 'warning'|'critical', details: string }}
72
+ */
73
+ export function evaluateTrigger(trigger, state) {
74
+ switch (trigger) {
75
+ case SAFETY_TRIGGERS.CONSECUTIVE_FAILURES:
76
+ return evaluateConsecutiveFailures(state);
77
+
78
+ case SAFETY_TRIGGERS.LOW_SCORE:
79
+ return evaluateLowScore(state);
80
+
81
+ case SAFETY_TRIGGERS.CRITICAL_RISK:
82
+ return evaluateCriticalRisk(state);
83
+
84
+ case SAFETY_TRIGGERS.TIMEOUT:
85
+ return evaluateTimeout(state);
86
+
87
+ case SAFETY_TRIGGERS.GOTCHA_SPIKE:
88
+ return evaluateGotchaSpike(state);
89
+
90
+ default:
91
+ return {
92
+ triggered: false,
93
+ severity: 'warning',
94
+ details: 'Unknown trigger',
95
+ };
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Get recommended action when safety net triggers.
101
+ * @param {object[]} triggers - Triggered safety conditions
102
+ * @returns {{ action: 'pause'|'warn'|'abort', reason: string, resumable: boolean }}
103
+ */
104
+ export function getRecommendedAction(triggers) {
105
+ if (triggers.length === 0) {
106
+ return {
107
+ action: 'warn',
108
+ reason: 'No safety triggers detected',
109
+ resumable: true,
110
+ };
111
+ }
112
+
113
+ // Check for critical severity
114
+ const hasCritical = triggers.some(t => t.severity === 'critical');
115
+
116
+ if (hasCritical) {
117
+ const criticalTriggers = triggers.filter(t => t.severity === 'critical');
118
+ return {
119
+ action: 'pause',
120
+ reason: `Critical safety conditions: ${criticalTriggers.map(t => t.details).join('; ')}`,
121
+ resumable: true,
122
+ };
123
+ }
124
+
125
+ // Only warnings
126
+ return {
127
+ action: 'warn',
128
+ reason: `Safety warnings: ${triggers.map(t => t.details).join('; ')}`,
129
+ resumable: true,
130
+ };
131
+ }
132
+
133
+ /**
134
+ * Evaluate consecutive failures trigger.
135
+ * @param {object} state
136
+ * @returns {{ triggered: boolean, severity: string, details: string }}
137
+ */
138
+ function evaluateConsecutiveFailures(state) {
139
+ const threshold = 3;
140
+ const failures = state.consecutiveFailures || 0;
141
+
142
+ if (failures >= threshold) {
143
+ return {
144
+ triggered: true,
145
+ severity: 'critical',
146
+ details: `${failures} consecutive gate failures (threshold: ${threshold})`,
147
+ };
148
+ }
149
+
150
+ return {
151
+ triggered: false,
152
+ severity: 'warning',
153
+ details: 'No consecutive failures',
154
+ };
155
+ }
156
+
157
+ /**
158
+ * Evaluate low score trigger.
159
+ * @param {object} state
160
+ * @returns {{ triggered: boolean, severity: string, details: string }}
161
+ */
162
+ function evaluateLowScore(state) {
163
+ const threshold = 70;
164
+ const score = state.lastScore;
165
+
166
+ if (typeof score !== 'number') {
167
+ return {
168
+ triggered: false,
169
+ severity: 'warning',
170
+ details: 'No score available',
171
+ };
172
+ }
173
+
174
+ if (score < threshold) {
175
+ return {
176
+ triggered: true,
177
+ severity: 'warning',
178
+ details: `Low quality score: ${score}% (threshold: ${threshold}%)`,
179
+ };
180
+ }
181
+
182
+ return {
183
+ triggered: false,
184
+ severity: 'warning',
185
+ details: 'Score within acceptable range',
186
+ };
187
+ }
188
+
189
+ /**
190
+ * Evaluate critical risk trigger.
191
+ * @param {object} state
192
+ * @returns {{ triggered: boolean, severity: string, details: string }}
193
+ */
194
+ function evaluateCriticalRisk(state) {
195
+ const riskFlags = state.riskFlags || [];
196
+
197
+ // Check for critical risk keywords
198
+ const criticalFlags = riskFlags.filter(flag =>
199
+ CRITICAL_RISK_KEYWORDS.some(keyword =>
200
+ flag.toLowerCase().includes(keyword.toLowerCase())
201
+ )
202
+ );
203
+
204
+ if (criticalFlags.length > 0) {
205
+ return {
206
+ triggered: true,
207
+ severity: 'critical',
208
+ details: `Critical risks detected: ${criticalFlags.join(', ')}`,
209
+ };
210
+ }
211
+
212
+ return {
213
+ triggered: false,
214
+ severity: 'warning',
215
+ details: 'No critical risks detected',
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Evaluate timeout trigger.
221
+ * @param {object} state
222
+ * @returns {{ triggered: boolean, severity: string, details: string }}
223
+ */
224
+ function evaluateTimeout(state) {
225
+ const { agentDurationMs, timeoutMs } = state;
226
+
227
+ if (typeof agentDurationMs !== 'number' || typeof timeoutMs !== 'number') {
228
+ return {
229
+ triggered: false,
230
+ severity: 'warning',
231
+ details: 'No timeout configured',
232
+ };
233
+ }
234
+
235
+ if (agentDurationMs >= timeoutMs) {
236
+ const minutes = Math.round(agentDurationMs / 60000);
237
+ const maxMinutes = Math.round(timeoutMs / 60000);
238
+ return {
239
+ triggered: true,
240
+ severity: 'critical',
241
+ details: `Agent timeout: ${minutes}min (max: ${maxMinutes}min)`,
242
+ };
243
+ }
244
+
245
+ return {
246
+ triggered: false,
247
+ severity: 'warning',
248
+ details: 'Agent within time limits',
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Evaluate gotcha spike trigger.
254
+ * @param {object} state
255
+ * @returns {{ triggered: boolean, severity: string, details: string }}
256
+ */
257
+ function evaluateGotchaSpike(state) {
258
+ const threshold = 5;
259
+ const gotchas = state.sessionGotchas || 0;
260
+
261
+ if (gotchas >= threshold) {
262
+ return {
263
+ triggered: true,
264
+ severity: 'warning',
265
+ details: `High gotcha count: ${gotchas} (threshold: ${threshold})`,
266
+ };
267
+ }
268
+
269
+ return {
270
+ triggered: false,
271
+ severity: 'warning',
272
+ details: 'Gotcha count within limits',
273
+ };
274
+ }
275
+
276
+ /**
277
+ * Get critical risk keywords list.
278
+ * @returns {string[]}
279
+ */
280
+ export function getCriticalRiskKeywords() {
281
+ return [...CRITICAL_RISK_KEYWORDS];
282
+ }
283
+
284
+ /**
285
+ * Check if a risk flag contains critical keywords.
286
+ * @param {string} flag
287
+ * @returns {boolean}
288
+ */
289
+ export function isCriticalRisk(flag) {
290
+ return CRITICAL_RISK_KEYWORDS.some(keyword =>
291
+ flag.toLowerCase().includes(keyword.toLowerCase())
292
+ );
293
+ }
294
+
295
+ /**
296
+ * Build safety report for user.
297
+ * @param {object} state
298
+ * @returns {{ status: string, message: string, triggers: object[] }}
299
+ */
300
+ export function buildSafetyReport(state) {
301
+ const result = checkSafety(state);
302
+
303
+ let status;
304
+ let message;
305
+
306
+ if (result.safe) {
307
+ status = 'safe';
308
+ message = 'All safety checks passed. Pipeline can continue.';
309
+ } else {
310
+ const action = getRecommendedAction(result.triggers);
311
+ status = action.action === 'pause' ? 'unsafe' : 'warning';
312
+ message = action.reason;
313
+ }
314
+
315
+ return {
316
+ status,
317
+ message,
318
+ triggers: result.triggers,
319
+ };
320
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Bracket Tracker — Pure arithmetic for context window management.
3
+ *
4
+ * Brackets determine how much context to inject:
5
+ * FRESH (60-100%) → All 5 layers active
6
+ * MODERATE (40-60%) → L0 + L1 + L2 + L3 (skip L4 task detail)
7
+ * DEPLETED (25-40%) → L0 + L1 + L2 only
8
+ * CRITICAL (<25%) → L0 + L1 only (handoff mandatory)
9
+ */
10
+
11
+ const BRACKETS = {
12
+ FRESH: { min: 60, max: 100, layers: ['L0', 'L1', 'L2', 'L3', 'L4'], tokenBudget: 8000 },
13
+ MODERATE: { min: 40, max: 60, layers: ['L0', 'L1', 'L2', 'L3'], tokenBudget: 5000 },
14
+ DEPLETED: { min: 25, max: 40, layers: ['L0', 'L1', 'L2'], tokenBudget: 3000 },
15
+ CRITICAL: { min: 0, max: 25, layers: ['L0', 'L1'], tokenBudget: 1500 },
16
+ };
17
+
18
+ const MEMORY_LEVELS = {
19
+ FRESH: 'full',
20
+ MODERATE: 'chunks',
21
+ DEPLETED: 'metadata',
22
+ CRITICAL: 'none',
23
+ };
24
+
25
+ /**
26
+ * Calculate bracket from remaining context percentage.
27
+ * @param {number} remainingPercent - 0 to 100
28
+ * @returns {{ bracket: string, activeLayers: string[], tokenBudget: number, memoryLevel: string, handoffRequired: boolean }}
29
+ */
30
+ export function calculateBracket(remainingPercent) {
31
+ const pct = Math.max(0, Math.min(100, remainingPercent));
32
+
33
+ let name = 'CRITICAL';
34
+ if (pct >= 60) name = 'FRESH';
35
+ else if (pct >= 40) name = 'MODERATE';
36
+ else if (pct >= 25) name = 'DEPLETED';
37
+
38
+ const def = BRACKETS[name];
39
+ return {
40
+ bracket: name,
41
+ activeLayers: [...def.layers],
42
+ tokenBudget: def.tokenBudget,
43
+ memoryLevel: MEMORY_LEVELS[name],
44
+ handoffRequired: pct < 15,
45
+ remainingPercent: pct,
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Estimate remaining context percentage from prompt/turn count.
51
+ * Heuristic: each turn consumes ~2-4% of context window.
52
+ * @param {number} turnCount - Number of conversation turns so far
53
+ * @param {number} [maxTurns=40] - Estimated max turns before context exhaustion
54
+ * @returns {number} Estimated remaining percentage (0-100)
55
+ */
56
+ export function estimateRemaining(turnCount, maxTurns = 40) {
57
+ const used = Math.min(turnCount, maxTurns) / maxTurns;
58
+ return Math.round((1 - used) * 100);
59
+ }
60
+
61
+ /**
62
+ * Check if a specific layer is active for a given bracket.
63
+ * @param {string} bracket - FRESH, MODERATE, DEPLETED, or CRITICAL
64
+ * @param {string} layer - L0, L1, L2, L3, or L4
65
+ * @returns {boolean}
66
+ */
67
+ export function isLayerActive(bracket, layer) {
68
+ const def = BRACKETS[bracket];
69
+ if (!def) return false;
70
+ return def.layers.includes(layer);
71
+ }
72
+
73
+ /**
74
+ * Get all bracket definitions (for display/dashboard).
75
+ * @returns {Record<string, { min: number, max: number, layers: string[], tokenBudget: number }>}
76
+ */
77
+ export function getBracketDefinitions() {
78
+ return { ...BRACKETS };
79
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Domain Loader — Parses YAML domain files for context injection.
3
+ *
4
+ * Domain files live in chati.dev/domains/ and contain rules/context
5
+ * that PRISM injects into agent prompts based on the active layer.
6
+ */
7
+
8
+ import { existsSync, readFileSync, readdirSync } from 'fs';
9
+ import { join, basename } from 'path';
10
+ import yaml from 'js-yaml';
11
+
12
+ /**
13
+ * Load a single domain YAML file.
14
+ * @param {string} filePath - Absolute path to YAML file
15
+ * @returns {{ loaded: boolean, data: object|null, error: string|null }}
16
+ */
17
+ export function loadDomainFile(filePath) {
18
+ if (!existsSync(filePath)) {
19
+ return { loaded: false, data: null, error: `File not found: ${filePath}` };
20
+ }
21
+ try {
22
+ const raw = readFileSync(filePath, 'utf-8');
23
+ const data = yaml.load(raw) || {};
24
+ return { loaded: true, data, error: null };
25
+ } catch (err) {
26
+ return { loaded: false, data: null, error: `Parse error in ${filePath}: ${err.message}` };
27
+ }
28
+ }
29
+
30
+ /**
31
+ * Load all agent domain files from chati.dev/domains/agents/.
32
+ * @param {string} domainsDir - Path to chati.dev/domains/
33
+ * @returns {Map<string, object>} Map of agentName → domain data
34
+ */
35
+ export function loadAgentDomains(domainsDir) {
36
+ const agentsDir = join(domainsDir, 'agents');
37
+ const domains = new Map();
38
+
39
+ if (!existsSync(agentsDir)) return domains;
40
+
41
+ const files = readdirSync(agentsDir).filter(f => f.endsWith('.yaml'));
42
+ for (const file of files) {
43
+ const name = basename(file, '.yaml');
44
+ const result = loadDomainFile(join(agentsDir, file));
45
+ if (result.loaded) {
46
+ domains.set(name, result.data);
47
+ }
48
+ }
49
+ return domains;
50
+ }
51
+
52
+ /**
53
+ * Load all workflow domain files from chati.dev/domains/workflows/.
54
+ * @param {string} domainsDir - Path to chati.dev/domains/
55
+ * @returns {Map<string, object>} Map of workflowName → domain data
56
+ */
57
+ export function loadWorkflowDomains(domainsDir) {
58
+ const workflowsDir = join(domainsDir, 'workflows');
59
+ const domains = new Map();
60
+
61
+ if (!existsSync(workflowsDir)) return domains;
62
+
63
+ const files = readdirSync(workflowsDir).filter(f => f.endsWith('.yaml'));
64
+ for (const file of files) {
65
+ const name = basename(file, '.yaml');
66
+ const result = loadDomainFile(join(workflowsDir, file));
67
+ if (result.loaded) {
68
+ domains.set(name, result.data);
69
+ }
70
+ }
71
+ return domains;
72
+ }
73
+
74
+ /**
75
+ * Load the constitution domain (extracted rules from constitution.md).
76
+ * @param {string} domainsDir - Path to chati.dev/domains/
77
+ * @returns {object|null}
78
+ */
79
+ export function loadConstitutionDomain(domainsDir) {
80
+ const result = loadDomainFile(join(domainsDir, 'constitution.yaml'));
81
+ return result.loaded ? result.data : null;
82
+ }
83
+
84
+ /**
85
+ * Load the global domain (coding standards, bracket rules).
86
+ * @param {string} domainsDir - Path to chati.dev/domains/
87
+ * @returns {object|null}
88
+ */
89
+ export function loadGlobalDomain(domainsDir) {
90
+ const result = loadDomainFile(join(domainsDir, 'global.yaml'));
91
+ return result.loaded ? result.data : null;
92
+ }
93
+
94
+ /**
95
+ * Extract rules array from a domain object.
96
+ * Domain files have a `rules` key with an array of rule objects.
97
+ * @param {object} domain - Parsed domain object
98
+ * @returns {Array<{ id: string, text: string, priority: string }>}
99
+ */
100
+ export function extractRules(domain) {
101
+ if (!domain || !Array.isArray(domain.rules)) return [];
102
+ return domain.rules.map(r => ({
103
+ id: r.id || 'unknown',
104
+ text: r.text || r.rule || '',
105
+ priority: r.priority || 'normal',
106
+ }));
107
+ }
@@ -0,0 +1,144 @@
1
+ /**
2
+ * PRISM Context Engine — 5-layer context injection pipeline.
3
+ *
4
+ * Orchestrates L0-L4 layers, respects bracket constraints,
5
+ * and produces formatted XML context for agent prompts.
6
+ *
7
+ * Pipeline: bracket calculation → layer processing → formatting → output
8
+ */
9
+
10
+ import { calculateBracket, isLayerActive } from './bracket-tracker.js';
11
+ import { processL0 } from './layers/l0-constitution.js';
12
+ import { processL1 } from './layers/l1-global.js';
13
+ import { processL2 } from './layers/l2-agent.js';
14
+ import { processL3 } from './layers/l3-workflow.js';
15
+ import { processL4 } from './layers/l4-task.js';
16
+ import { formatContext } from './formatter.js';
17
+
18
+ const LAYER_TIMEOUT_MS = 100;
19
+
20
+ /**
21
+ * Run the PRISM context engine pipeline.
22
+ *
23
+ * @param {object} input
24
+ * @param {string} input.domainsDir - Path to chati.dev/domains/
25
+ * @param {number} input.remainingPercent - Context window remaining (0-100)
26
+ * @param {string} [input.mode] - Current mode (clarity, build, deploy)
27
+ * @param {string} [input.agent] - Active agent name
28
+ * @param {string} [input.workflow] - Active workflow name
29
+ * @param {string} [input.pipelinePosition] - Current pipeline step
30
+ * @param {string} [input.taskId] - Active task ID
31
+ * @param {object} [input.handoff] - Handoff data from previous agent
32
+ * @param {string[]} [input.artifacts] - Relevant artifact paths
33
+ * @param {string[]} [input.taskCriteria] - Active task criteria
34
+ * @returns {{ xml: string, bracket: object, layers: object[], errors: string[] }}
35
+ */
36
+ export function runPrism(input) {
37
+ const errors = [];
38
+ const layers = [];
39
+
40
+ // 1. Calculate bracket
41
+ const bracket = calculateBracket(input.remainingPercent);
42
+
43
+ // 2. Build context for layer processors
44
+ const ctx = {
45
+ domainsDir: input.domainsDir,
46
+ mode: input.mode || 'clarity',
47
+ bracket: bracket.bracket,
48
+ agent: input.agent || null,
49
+ workflow: input.workflow || null,
50
+ pipelinePosition: input.pipelinePosition || null,
51
+ taskId: input.taskId || null,
52
+ handoff: input.handoff || {},
53
+ artifacts: input.artifacts || [],
54
+ taskCriteria: input.taskCriteria || [],
55
+ };
56
+
57
+ // 3. Process each active layer with timeout protection
58
+ const layerResults = {};
59
+
60
+ // L0 — Always active
61
+ const l0 = safeProcess('L0', () => processL0(ctx), errors);
62
+ if (l0) { layers.push(l0); layerResults.l0 = l0; }
63
+
64
+ // L1 — Always active
65
+ if (isLayerActive(bracket.bracket, 'L1')) {
66
+ const l1 = safeProcess('L1', () => processL1(ctx), errors);
67
+ if (l1) { layers.push(l1); layerResults.l1 = l1; }
68
+ }
69
+
70
+ // L2 — Agent layer
71
+ if (isLayerActive(bracket.bracket, 'L2') && ctx.agent) {
72
+ const l2 = safeProcess('L2', () => processL2(ctx), errors);
73
+ if (l2) { layers.push(l2); layerResults.l2 = l2; }
74
+ }
75
+
76
+ // L3 — Workflow/Pipeline layer
77
+ if (isLayerActive(bracket.bracket, 'L3') && ctx.workflow) {
78
+ const l3 = safeProcess('L3', () => processL3(ctx), errors);
79
+ if (l3) { layers.push(l3); layerResults.l3 = l3; }
80
+ }
81
+
82
+ // L4 — Task layer
83
+ if (isLayerActive(bracket.bracket, 'L4') && ctx.taskId) {
84
+ const l4 = safeProcess('L4', () => processL4(ctx), errors);
85
+ if (l4) { layers.push(l4); layerResults.l4 = l4; }
86
+ }
87
+
88
+ // 4. Format output
89
+ const xml = formatContext({
90
+ bracket: bracket.bracket,
91
+ tokenBudget: bracket.tokenBudget,
92
+ ...layerResults,
93
+ });
94
+
95
+ return {
96
+ xml,
97
+ bracket,
98
+ layers,
99
+ errors,
100
+ layerCount: layers.length,
101
+ };
102
+ }
103
+
104
+ /**
105
+ * Run a layer processor with error protection.
106
+ * If the processor throws, capture the error and return null (graceful degradation).
107
+ */
108
+ function safeProcess(layerName, processFn, errors) {
109
+ try {
110
+ const start = Date.now();
111
+ const result = processFn();
112
+ const elapsed = Date.now() - start;
113
+
114
+ if (elapsed > LAYER_TIMEOUT_MS) {
115
+ errors.push(`${layerName} exceeded timeout (${elapsed}ms > ${LAYER_TIMEOUT_MS}ms)`);
116
+ }
117
+
118
+ return result;
119
+ } catch (err) {
120
+ errors.push(`${layerName} failed: ${err.message}`);
121
+ return null;
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Get a summary of PRISM capabilities for display.
127
+ * @returns {{ layers: number, brackets: string[], features: string[] }}
128
+ */
129
+ export function getPrismInfo() {
130
+ return {
131
+ name: 'PRISM',
132
+ version: '1.0.0',
133
+ layers: 5,
134
+ layerNames: ['L0 Constitution', 'L1 Global', 'L2 Agent', 'L3 Workflow', 'L4 Task'],
135
+ brackets: ['FRESH', 'MODERATE', 'DEPLETED', 'CRITICAL'],
136
+ features: [
137
+ 'Priority-based context truncation',
138
+ 'Per-layer timeout protection',
139
+ 'Graceful degradation on layer failure',
140
+ 'XML structured output',
141
+ 'Bracket-aware layer activation',
142
+ ],
143
+ };
144
+ }