chati-dev 3.3.2 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -266,7 +266,7 @@ Three systems operate transparently behind the pipeline:
266
266
 
267
267
  | System | What it does |
268
268
  |--------|-------------|
269
- | **Context Engine (PRISM)** | Injects the right context at the right time. 5 layers of context (from system-wide rules down to specific task details). Tracks how much context space remains and adapts automatically. |
269
+ | **Context Engine (PRISM)** | Injects the right context at the right time. 6 layers of context (from constitution rules down to keyword-matched domain rules). Uses Progressive Reinforcement: as context degrades, injection budgets INCREASE (1.5% to 5.0% of provider context window). Provider-aware for Claude (200K), Gemini (1M), and Codex (128K). |
270
270
  | **Memory System (RECALL)** | Remembers decisions, gotchas, and lessons across sessions. Organized into 4 sectors: what happened (episodic), what we know (semantic), how we do things (procedural), and what we learned (reflective). |
271
271
  | **Decision Engine (COMPASS)** | Before creating something new, checks if a similar component already exists. Decides whether to reuse, adapt, or create from scratch. Keeps a registry of all project entities. |
272
272
 
@@ -390,8 +390,8 @@ your-project/
390
390
  │ │ ├── quality/ # QA-Planning, QA-Implementation
391
391
  │ │ ├── build/ # Dev
392
392
  │ │ └── deploy/ # DevOps
393
- │ ├── workflows/ # 6 workflow blueprints
394
- │ ├── templates/ # 6 artifact templates
393
+ │ ├── workflows/ # 7 workflow blueprints
394
+ │ ├── templates/ # 10 artifact templates
395
395
  │ ├── schemas/ # 5 JSON schemas
396
396
  │ ├── intelligence/ # PRISM, RECALL, COMPASS specs
397
397
  │ ├── domains/ # Per-agent and per-workflow configs
@@ -454,5 +454,5 @@ For security concerns, see our [Security Policy](.github/SECURITY.md).
454
454
 
455
455
  <p align="center">
456
456
  <sub>Built with structure, validated by agents, governed by constitution.</sub><br>
457
- <sub>Chati.dev &copy; 2026</sub>
457
+ <sub>Chati.dev v4.0 "Pegasus" &copy; 2026</sub>
458
458
  </p>
@@ -1,8 +1,8 @@
1
1
  # chati.dev Configuration
2
- version: "3.3.2"
2
+ version: "4.0.0"
3
3
  installed_at: "2026-02-07T10:00:00Z"
4
- updated_at: "2026-03-03T00:00:00Z"
5
- installer_version: "3.3.2"
4
+ updated_at: "2026-03-17T00:00:00Z"
5
+ installer_version: "4.0.0"
6
6
  project_type: greenfield
7
7
  language: en
8
8
  ides: [claude-code]
@@ -28,6 +28,21 @@ providers:
28
28
  # provider: gemini
29
29
  # model: pro
30
30
 
31
+ # Feature flags — toggle new capabilities (v4.0 Pegasus)
32
+ features:
33
+ hybrid_budget: true
34
+ anti_dash: true
35
+ rate_limiter_integration: true
36
+ l5_keywords: true
37
+ prompt_size_guard: true
38
+ ids_decision_engine: true
39
+ surface_criteria: true
40
+ parallel_fallback: true
41
+ tool_mesh: true
42
+ tech_presets: true
43
+ doctor_autofix: true
44
+ brandbook: true
45
+
31
46
  # Telemetry — opt-in anonymous usage tracking
32
47
  telemetry:
33
48
  enabled: true
@@ -32,13 +32,13 @@ Chati.dev is a planning-first AI-assisted orchestration system that coordinates
32
32
 
33
33
  ### Exclusion List
34
34
 
35
- The following items from source projects are explicitly excluded from Chati.dev:
36
- - CI/CD pipelines, release scripts, publish scripts from source projects
35
+ The following items are explicitly out of scope for Chati.dev:
36
+ - CI/CD pipelines, release scripts, publish scripts (contributor tooling)
37
37
  - Husky/lint-staged configs (contributor tooling)
38
38
  - npm package configs (.npmignore, .npmrc)
39
39
  - Dashboard web UI (separate product)
40
40
  - Monitor Server (separate product)
41
- - Source project installer packages
41
+ - Standalone installer packages (CLI handles installation)
42
42
  - execution-n8n skill (removed — code-only focus)
43
43
  - Generic tasks redundant with agent self-validation protocols
44
44
  - Duplicate checklists already covered by agent self-validation
@@ -270,7 +270,8 @@ The pipeline operates in three execution modes that control agent permissions. M
270
270
  1. The orchestrator SHALL calculate the context bracket (FRESH, MODERATE, DEPLETED, CRITICAL) before every agent interaction.
271
271
 
272
272
  2. Context injection layers SHALL be reduced according to bracket level:
273
- - FRESH/MODERATE: All 5 layers (L0-L4)
273
+ - FRESH: All 6 layers (L0-L5)
274
+ - MODERATE: L0 + L1 + L2 + L3 + L5 (skip L4 task detail)
274
275
  - DEPLETED: L0 (Constitution) + L1 (Mode) + L2 (Agent) only
275
276
  - CRITICAL: L0 (Constitution) + L1 (Mode) only
276
277
 
@@ -280,11 +281,12 @@ The pipeline operates in three execution modes that control agent permissions. M
280
281
 
281
282
  4. The Constitution (L0) and Mode governance (L1) are NON-NEGOTIABLE and SHALL be injected in ALL brackets, including CRITICAL.
282
283
 
283
- 5. Token budgets per bracket:
284
- - FRESH: 2500 tokens maximum
285
- - MODERATE: 2000 tokens maximum
286
- - DEPLETED: 1500 tokens maximum
287
- - CRITICAL: 800 tokens maximum
284
+ 5. Token budgets per bracket (Progressive Reinforcement Model — as context degrades, reinforcement increases):
285
+ - Budgets are expressed as a percentage of the provider's context window
286
+ - FRESH: 1.5% of context window (Claude: 3000, Gemini: 15000, Codex: 1920)
287
+ - MODERATE: 2.5% of context window (Claude: 5000, Gemini: 25000, Codex: 3200)
288
+ - DEPLETED: 4.0% of context window (Claude: 8000, Gemini: 40000, Codex: 5120)
289
+ - CRITICAL: 5.0% of context window (Claude: 10000, Gemini: 50000, Codex: 6400)
288
290
 
289
291
  6. Autonomous spawn capability varies by IDE:
290
292
  - Full autonomy: Claude Code, AntiGravity, Gemini CLI
@@ -472,5 +474,5 @@ When multiple CLI providers are enabled, the system SHALL coordinate agent execu
472
474
 
473
475
  ---
474
476
 
475
- *Chati.dev Constitution v3.3.0 — 19 Articles + Preamble*
477
+ *Chati.dev Constitution v4.0.0 — 19 Articles + Preamble*
476
478
  *All agents are bound by this Constitution. Violations are enforced per article.*
@@ -10,6 +10,8 @@ Extracted from `chati.dev/constitution.md` (19 Articles). Read the full constitu
10
10
  ## Context Bracket (Article XII)
11
11
  - 4 brackets: FRESH, MODERATE, DEPLETED, CRITICAL
12
12
  - CRITICAL = Constitution + Global layers only (no agent/task context)
13
+ - Progressive Reinforcement: budget INCREASES as context degrades (1.5% → 2.5% → 4.0% → 5.0% of provider context window)
14
+ - Provider-aware: budgets scale proportionally to context window size (Claude 200K, Gemini 1M, Codex 128K)
13
15
  - Handoff before bracket drops below 15%
14
16
 
15
17
  ## Memory (Article XIII)
@@ -1,7 +1,7 @@
1
1
  # Chati.dev System Context
2
2
 
3
3
  ## Framework
4
- - **Version**: 3.3.0
4
+ - **Version**: 4.0.0
5
5
  - **Agents**: 13 (12 specialized + orchestrator)
6
6
  - **Constitution**: 19 Articles + Preamble
7
7
  - **Quality**: 5 pipeline gates + 3-tier verdicts (APPROVED / NEEDS_REVISION / BLOCKED)
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Purpose
4
4
 
5
- The Context Engine monitors context window usage and adapts what information is injected into each agent's prompt. As the context fills, fewer layers are injected to preserve quality for critical operations.
5
+ The Context Engine monitors context window usage and adapts what information is injected into each agent's prompt. As the context fills, fewer layers are injected to maintain focus, while per-layer reinforcement INCREASES to compensate for the model forgetting initial instructions (Progressive Reinforcement Model).
6
6
 
7
7
  ---
8
8
 
@@ -12,16 +12,16 @@ Four brackets define behavior based on remaining context:
12
12
 
13
13
  | Bracket | Context Remaining | Behavior |
14
14
  |---------|-------------------|----------|
15
- | **FRESH** | 60-100% | All protocols active. Minimal injection — context is plentiful. |
16
- | **MODERATE** | 40-60% | Normal protocols. Summarize long outputs. Standard memory retrieval. |
17
- | **DEPLETED** | 25-40% | Reinforce Constitution + Agent scope. Skip optional patterns. Recover context via memory. |
18
- | **CRITICAL** | <25% | Constitution + Agent scope only. Trigger handoff to new session. Full memory dump for continuity. |
15
+ | **FRESH** | 60-100% | All 6 layers active. Minimal reinforcement (1.5%) — context is plentiful. No memory injection. |
16
+ | **MODERATE** | 40-60% | 5 layers (skip L4). Growing reinforcement (2.5%) model starting to forget. Metadata memory. |
17
+ | **DEPLETED** | 25-40% | 3 layers (L0-L2). Heavy reinforcement (4.0%) significant context loss. Chunk memory recovery. |
18
+ | **CRITICAL** | <25% | 2 layers (L0-L1). Maximum reinforcement (5.0%) last interactions before handoff. Full memory dump. |
19
19
 
20
20
  ---
21
21
 
22
22
  ## Layered Context Injection
23
23
 
24
- The orchestrator injects context through 5 hierarchical layers:
24
+ The orchestrator injects context through 6 hierarchical layers:
25
25
 
26
26
  | Layer | Name | Source | When Active |
27
27
  |-------|------|--------|-------------|
@@ -30,15 +30,18 @@ The orchestrator injects context through 5 hierarchical layers:
30
30
  | **L2** | Agent Scope | `chati.dev/agents/{agent}/` — mission, inputs, outputs, criteria | When agent is active |
31
31
  | **L3** | Pipeline State | `.chati/session.yaml` — pipeline position, scores, backlog | When session is active |
32
32
  | **L4** | Task Context | Active artifact + previous agent's handoff | When task is active |
33
+ | **L5** | Keywords | `chati.dev/domains/keywords/` — dynamic rules from user prompt | FRESH + MODERATE only |
33
34
 
34
35
  ### Layer Activation by Bracket
35
36
 
36
- | Bracket | Active Layers | Approx. Token Budget |
37
- |---------|--------------|---------------------|
38
- | FRESH | L0, L1, L2, L3, L4 | ~2500 tokens |
39
- | MODERATE | L0, L1, L2, L3, L4 | ~2000 tokens |
40
- | DEPLETED | L0, L1, L2 | ~1500 tokens |
41
- | CRITICAL | L0, L1 | ~800 tokens |
37
+ | Bracket | Active Layers | Budget Ratio | Claude (200K) | Gemini (1M) | Codex (128K) |
38
+ |---------|--------------|-------------|---------------|-------------|--------------|
39
+ | FRESH | L0, L1, L2, L3, L4, L5 | 1.5% | 3,000 | 15,000 | 1,920 |
40
+ | MODERATE | L0, L1, L2, L3, L5 | 2.5% | 5,000 | 25,000 | 3,200 |
41
+ | DEPLETED | L0, L1, L2 | 4.0% | 8,000 | 40,000 | 5,120 |
42
+ | CRITICAL | L0, L1 | 5.0% | 10,000 | 50,000 | 6,400 |
43
+
44
+ Budgets are proportional to the provider's context window. As context degrades, fewer layers are active (focus) but each active layer receives MORE reinforcement tokens (strength).
42
45
 
43
46
  ---
44
47
 
@@ -145,12 +148,14 @@ If Smart Continuation is insufficient, the orchestrator spawns a new session:
145
148
 
146
149
  ## Integration with Memory Layer
147
150
 
151
+ Memory injection follows the same progressive reinforcement principle: as context degrades, memory becomes MORE important.
152
+
148
153
  | Bracket | Memory Level | Behavior |
149
154
  |---------|-------------|----------|
150
- | FRESH | None | Context is sufficient — no memory injection |
151
- | MODERATE | L1 Metadata | Light reminder of relevant memories (~50 tokens) |
152
- | DEPLETED | L2 Chunks | Context recovery via memory summaries (~200 tokens) |
153
- | CRITICAL | L3 Full | Full memory dump for session handoff (~1000+ tokens) |
155
+ | FRESH | none | Context is sufficient — no memory injection needed |
156
+ | MODERATE | metadata | Light reminder of relevant memories (~50 tokens) |
157
+ | DEPLETED | chunks | Context recovery via memory summaries (~200 tokens) |
158
+ | CRITICAL | full | Full memory dump for session handoff (~1000+ tokens) |
154
159
 
155
160
  ---
156
161
 
@@ -160,7 +165,7 @@ If Smart Continuation is insufficient, the orchestrator spawns a new session:
160
165
 
161
166
  ---
162
167
 
163
- *Context Engine v1.0 — Chati.dev Intelligence Layer*
168
+ *Context Engine v2.0 — Chati.dev Intelligence Layer (Progressive Reinforcement Model)*
164
169
 
165
170
  ---
166
171
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chati-dev",
3
- "version": "3.3.2",
3
+ "version": "4.0.0",
4
4
  "description": "AI-Powered Multi-Agent Orchestration System — Structured vibe coding for Full Stack Development",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,77 @@
1
+ /**
2
+ * @fileoverview Doctor check: Agent definition files.
3
+ *
4
+ * Validates that all 13 agent definition files exist with valid structure.
5
+ */
6
+
7
+ import { readFileSync, existsSync } from 'fs';
8
+ import { join } from 'path';
9
+
10
+ const EXPECTED_AGENTS = {
11
+ 'discover/greenfield-wu.md': 'greenfield-wu',
12
+ 'discover/brownfield-wu.md': 'brownfield-wu',
13
+ 'discover/brief.md': 'brief',
14
+ 'plan/detail.md': 'detail',
15
+ 'plan/architect.md': 'architect',
16
+ 'plan/ux.md': 'ux',
17
+ 'plan/phases.md': 'phases',
18
+ 'plan/tasks.md': 'tasks',
19
+ 'quality/qa-planning.md': 'qa-planning',
20
+ 'quality/qa-implementation.md': 'qa-implementation',
21
+ 'build/dev.md': 'dev',
22
+ 'deploy/devops.md': 'devops',
23
+ };
24
+
25
+ /**
26
+ * Run agents check.
27
+ *
28
+ * @param {string} frameworkDir - Path to framework directory
29
+ * @returns {{ pass: boolean, details: string, severity: string, fixable: boolean, fixId: string|null }}
30
+ */
31
+ export function checkAgents(frameworkDir) {
32
+ const agentsDir = join(frameworkDir, 'agents');
33
+ const orchestratorDir = join(frameworkDir, 'orchestrator');
34
+
35
+ const missing = [];
36
+ const invalid = [];
37
+
38
+ for (const [relPath, name] of Object.entries(EXPECTED_AGENTS)) {
39
+ const fullPath = join(agentsDir, relPath);
40
+ if (!existsSync(fullPath)) {
41
+ missing.push(name);
42
+ continue;
43
+ }
44
+ try {
45
+ const content = readFileSync(fullPath, 'utf8');
46
+ if (!content.includes('## Identity') && !content.includes('## Mission')) {
47
+ invalid.push(name);
48
+ }
49
+ } catch {
50
+ invalid.push(name);
51
+ }
52
+ }
53
+
54
+ // Check orchestrator
55
+ const orchPath = join(orchestratorDir, 'chati.md');
56
+ if (!existsSync(orchPath)) {
57
+ missing.push('orchestrator');
58
+ }
59
+
60
+ const total = Object.keys(EXPECTED_AGENTS).length + 1;
61
+ const found = total - missing.length;
62
+
63
+ if (missing.length > 0 || invalid.length > 0) {
64
+ const issues = [];
65
+ if (missing.length) issues.push(`missing: ${missing.join(', ')}`);
66
+ if (invalid.length) issues.push(`invalid: ${invalid.join(', ')}`);
67
+ return {
68
+ pass: false,
69
+ details: `${found}/${total} agents found; ${issues.join('; ')}`,
70
+ severity: missing.length > 0 ? 'critical' : 'warning',
71
+ fixable: false,
72
+ fixId: null,
73
+ };
74
+ }
75
+
76
+ return { pass: true, details: `${found}/${total} agents present`, severity: 'info', fixable: false, fixId: null };
77
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @fileoverview Doctor check: Constitution integrity.
3
+ *
4
+ * Validates constitution.md exists and contains the expected number of articles.
5
+ */
6
+
7
+ import { readFileSync, existsSync } from 'fs';
8
+ import { join } from 'path';
9
+
10
+ /**
11
+ * Run constitution integrity check.
12
+ *
13
+ * @param {string} frameworkDir - Path to framework directory
14
+ * @returns {{ pass: boolean, details: string, severity: string, fixable: boolean, fixId: string|null }}
15
+ */
16
+ export function checkConstitution(frameworkDir) {
17
+ const constPath = join(frameworkDir, 'constitution.md');
18
+ if (!existsSync(constPath)) {
19
+ return { pass: false, details: 'constitution.md not found', severity: 'critical', fixable: false, fixId: null };
20
+ }
21
+
22
+ try {
23
+ const content = readFileSync(constPath, 'utf8');
24
+ const articleMatches = content.match(/^## Article [IVXLCDM]+/gm) || [];
25
+ const articleCount = articleMatches.length;
26
+
27
+ if (articleCount < 10) {
28
+ return {
29
+ pass: false,
30
+ details: `Only ${articleCount} articles found (expected 10+)`,
31
+ severity: 'warning',
32
+ fixable: false,
33
+ fixId: null,
34
+ };
35
+ }
36
+
37
+ return { pass: true, details: `${articleCount} articles found`, severity: 'info', fixable: false, fixId: null };
38
+ } catch (err) {
39
+ return { pass: false, details: `Failed to read: ${err.message}`, severity: 'critical', fixable: false, fixId: null };
40
+ }
41
+ }
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Doctor check: Domain-Agent alignment.
3
+ *
4
+ * Validates that each agent domain YAML has a corresponding agent .md file.
5
+ */
6
+
7
+ import { existsSync, readdirSync } from 'fs';
8
+ import { join, basename } from 'path';
9
+
10
+ /**
11
+ * Run domain-agent alignment check.
12
+ *
13
+ * @param {string} frameworkDir - Path to framework directory
14
+ * @returns {{ pass: boolean, details: string, severity: string, fixable: boolean, fixId: string|null }}
15
+ */
16
+ export function checkDomainAlignment(frameworkDir) {
17
+ const domainsDir = join(frameworkDir, 'domains', 'agents');
18
+ if (!existsSync(domainsDir)) {
19
+ return { pass: false, details: 'domains/agents/ directory not found', severity: 'warning', fixable: false, fixId: null };
20
+ }
21
+
22
+ const domainFiles = readdirSync(domainsDir).filter(f => f.endsWith('.yaml'));
23
+ const mismatches = [];
24
+
25
+ for (const file of domainFiles) {
26
+ const agentName = basename(file, '.yaml');
27
+
28
+ // Orchestrator has a different path
29
+ if (agentName === 'orchestrator') {
30
+ if (!existsSync(join(frameworkDir, 'orchestrator', 'chati.md'))) {
31
+ mismatches.push('orchestrator');
32
+ }
33
+ continue;
34
+ }
35
+
36
+ // Other agents: check all category dirs
37
+ const agentDirs = ['agents/discover', 'agents/plan', 'agents/quality', 'agents/build', 'agents/deploy'];
38
+ const found = agentDirs.some(dir =>
39
+ existsSync(join(frameworkDir, dir, `${agentName}.md`))
40
+ );
41
+
42
+ if (!found) {
43
+ mismatches.push(agentName);
44
+ }
45
+ }
46
+
47
+ if (mismatches.length > 0) {
48
+ return {
49
+ pass: false,
50
+ details: `Mismatched agents: ${mismatches.join(', ')}`,
51
+ severity: 'warning',
52
+ fixable: false,
53
+ fixId: null,
54
+ };
55
+ }
56
+
57
+ return { pass: true, details: `${domainFiles.length} domain-agent pairs aligned`, severity: 'info', fixable: false, fixId: null };
58
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * @fileoverview Doctor check: PRISM Layer integrity.
3
+ *
4
+ * Validates that all 6 PRISM layers (L0-L5) have the required
5
+ * domain files and configuration in place.
6
+ */
7
+
8
+ import { existsSync, readdirSync, readFileSync } from 'fs';
9
+ import { join } from 'path';
10
+
11
+ /**
12
+ * Run PRISM layers integrity check.
13
+ *
14
+ * @param {string} frameworkDir - Path to framework directory
15
+ * @returns {{ pass: boolean, details: string, severity: string, fixable: boolean, fixId: string|null }}
16
+ */
17
+ export function checkPrismLayers(frameworkDir) {
18
+ const domainsDir = join(frameworkDir, 'domains');
19
+ if (!existsSync(domainsDir)) {
20
+ return { pass: false, details: 'domains/ directory not found', severity: 'critical', fixable: false, fixId: null };
21
+ }
22
+
23
+ const issues = [];
24
+
25
+ // L0: Constitution domain
26
+ const constitutionDomain = join(domainsDir, 'constitution.yaml');
27
+ if (!existsSync(constitutionDomain)) {
28
+ issues.push('L0: constitution.yaml missing');
29
+ }
30
+
31
+ // L1: Global domain
32
+ const globalDomain = join(domainsDir, 'global.yaml');
33
+ if (!existsSync(globalDomain)) {
34
+ issues.push('L1: global.yaml missing');
35
+ }
36
+
37
+ // L2: Agent domains
38
+ const agentsDir = join(domainsDir, 'agents');
39
+ if (!existsSync(agentsDir)) {
40
+ issues.push('L2: agents/ directory missing');
41
+ } else {
42
+ const agentDomains = readdirSync(agentsDir).filter(f => f.endsWith('.yaml'));
43
+ if (agentDomains.length < 12) {
44
+ issues.push(`L2: only ${agentDomains.length}/12 agent domains found`);
45
+ }
46
+ }
47
+
48
+ // L3: Workflow domains
49
+ const workflowsDir = join(domainsDir, 'workflows');
50
+ if (!existsSync(workflowsDir)) {
51
+ issues.push('L3: workflows/ directory missing');
52
+ }
53
+
54
+ // L4: Task context (checked via task domain files or inline)
55
+ // L4 is dynamic (loaded from handoff data), so we check that the task
56
+ // processing layer module exists
57
+ const l4Module = join(frameworkDir, '..', 'src', 'context', 'layers', 'l4-task.js');
58
+ if (!existsSync(l4Module)) {
59
+ issues.push('L4: l4-task.js module missing');
60
+ }
61
+
62
+ // L5: Keywords domains
63
+ const keywordsDir = join(domainsDir, 'keywords');
64
+ if (!existsSync(keywordsDir)) {
65
+ issues.push('L5: keywords/ directory missing');
66
+ } else {
67
+ const keywordFiles = readdirSync(keywordsDir).filter(f => f.endsWith('.yaml'));
68
+ if (keywordFiles.length === 0) {
69
+ issues.push('L5: no keyword domain files found');
70
+ }
71
+ }
72
+
73
+ if (issues.length > 0) {
74
+ return {
75
+ pass: false,
76
+ details: issues.join('; '),
77
+ severity: issues.some(i => i.includes('L0') || i.includes('L1')) ? 'critical' : 'warning',
78
+ fixable: false,
79
+ fixId: null,
80
+ };
81
+ }
82
+
83
+ return { pass: true, details: 'All 6 PRISM layers (L0-L5) validated', severity: 'info', fixable: false, fixId: null };
84
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * @fileoverview Doctor check: Entity Registry integrity.
3
+ *
4
+ * Validates entity-registry.yaml exists, parses correctly,
5
+ * entity count matches metadata, and all entity paths exist.
6
+ */
7
+
8
+ import { readFileSync, existsSync } from 'fs';
9
+ import { join } from 'path';
10
+ import yaml from 'js-yaml';
11
+
12
+ /**
13
+ * Run entity registry integrity check.
14
+ *
15
+ * @param {string} frameworkDir - Path to framework directory
16
+ * @returns {{ pass: boolean, details: string, severity: string, fixable: boolean, fixId: string|null }}
17
+ */
18
+ export function checkRegistry(frameworkDir) {
19
+ const regPath = join(frameworkDir, 'data', 'entity-registry.yaml');
20
+ if (!existsSync(regPath)) {
21
+ return { pass: false, details: 'entity-registry.yaml not found', severity: 'critical', fixable: false, fixId: null };
22
+ }
23
+
24
+ try {
25
+ const content = readFileSync(regPath, 'utf8');
26
+ const registry = yaml.load(content);
27
+
28
+ if (!registry || !registry.entities) {
29
+ return { pass: false, details: 'entity-registry.yaml has no entities section', severity: 'critical', fixable: false, fixId: null };
30
+ }
31
+
32
+ // Count all entities across all categories
33
+ let entityCount = 0;
34
+ for (const category of Object.values(registry.entities)) {
35
+ if (typeof category === 'object' && category !== null) {
36
+ entityCount += Object.keys(category).length;
37
+ }
38
+ }
39
+
40
+ const declaredCount = registry.metadata?.entity_count || 0;
41
+ if (declaredCount > 0 && entityCount !== declaredCount) {
42
+ return {
43
+ pass: false,
44
+ details: `Entity count mismatch: declared ${declaredCount}, actual ${entityCount}`,
45
+ severity: 'warning',
46
+ fixable: true,
47
+ fixId: 'registry-count',
48
+ };
49
+ }
50
+
51
+ return { pass: true, details: `${entityCount} entities registered`, severity: 'info', fixable: false, fixId: null };
52
+ } catch (err) {
53
+ return { pass: false, details: `Failed to parse: ${err.message}`, severity: 'critical', fixable: false, fixId: null };
54
+ }
55
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @fileoverview Doctor check: JSON Schema validation.
3
+ *
4
+ * Validates that all expected schema files exist and contain valid JSON.
5
+ */
6
+
7
+ import { readFileSync, existsSync, readdirSync } from 'fs';
8
+ import { join } from 'path';
9
+
10
+ const EXPECTED_SCHEMAS = [
11
+ 'session.schema.json',
12
+ 'config.schema.json',
13
+ 'context.schema.json',
14
+ 'memory.schema.json',
15
+ 'task.schema.json',
16
+ ];
17
+
18
+ /**
19
+ * Run schema validation check.
20
+ *
21
+ * @param {string} frameworkDir - Path to framework directory
22
+ * @returns {{ pass: boolean, details: string, severity: string, fixable: boolean, fixId: string|null }}
23
+ */
24
+ export function checkSchemas(frameworkDir) {
25
+ const schemasDir = join(frameworkDir, 'schemas');
26
+ if (!existsSync(schemasDir)) {
27
+ return { pass: false, details: 'schemas/ directory not found', severity: 'critical', fixable: false, fixId: null };
28
+ }
29
+
30
+ const missing = [];
31
+ const invalid = [];
32
+
33
+ for (const schema of EXPECTED_SCHEMAS) {
34
+ const schemaPath = join(schemasDir, schema);
35
+ if (!existsSync(schemaPath)) {
36
+ missing.push(schema);
37
+ continue;
38
+ }
39
+ try {
40
+ JSON.parse(readFileSync(schemaPath, 'utf8'));
41
+ } catch {
42
+ invalid.push(schema);
43
+ }
44
+ }
45
+
46
+ if (missing.length > 0 || invalid.length > 0) {
47
+ const issues = [];
48
+ if (missing.length) issues.push(`missing: ${missing.join(', ')}`);
49
+ if (invalid.length) issues.push(`invalid JSON: ${invalid.join(', ')}`);
50
+ return {
51
+ pass: false,
52
+ details: issues.join('; '),
53
+ severity: missing.length > 0 ? 'critical' : 'warning',
54
+ fixable: false,
55
+ fixId: null,
56
+ };
57
+ }
58
+
59
+ const actualSchemas = readdirSync(schemasDir).filter(f => f.endsWith('.json'));
60
+ return { pass: true, details: `${actualSchemas.length} schemas valid`, severity: 'info', fixable: false, fixId: null };
61
+ }