chati-dev 3.3.2 → 4.0.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.
- package/README.md +4 -4
- package/framework/agents/plan/ux.md +41 -1
- package/framework/config.yaml +18 -3
- package/framework/constitution.md +18 -10
- package/framework/context/governance.md +2 -0
- package/framework/context/root.md +1 -1
- package/framework/data/entity-registry.yaml +43 -3
- package/framework/domains/constitution.yaml +1 -1
- package/framework/domains/global.yaml +7 -3
- package/framework/domains/workflows/standard-flow.yaml +33 -0
- package/framework/i18n/en.yaml +11 -2
- package/framework/i18n/es.yaml +11 -2
- package/framework/i18n/fr.yaml +11 -2
- package/framework/i18n/pt.yaml +11 -2
- package/framework/intelligence/context-engine.md +22 -17
- package/framework/presets/nextjs.yaml +41 -0
- package/framework/presets/node-express.yaml +39 -0
- package/framework/presets/react-vite.yaml +37 -0
- package/framework/presets/supabase-fullstack.yaml +37 -0
- package/framework/templates/brandbook-tmpl.yaml +113 -0
- package/framework/templates/component-spec-tmpl.yaml +74 -0
- package/framework/templates/design-token-tmpl.yaml +55 -0
- package/framework/templates/icon-system-tmpl.yaml +95 -0
- package/package.json +1 -1
- package/scripts/bundle-framework.js +1 -1
- package/scripts/doctor/checks/agents.js +77 -0
- package/scripts/doctor/checks/constitution.js +41 -0
- package/scripts/doctor/checks/domain-alignment.js +58 -0
- package/scripts/doctor/checks/prism-layers.js +84 -0
- package/scripts/doctor/checks/registry.js +55 -0
- package/scripts/doctor/checks/schemas.js +61 -0
- package/scripts/doctor/fixes/reference-fix.js +100 -0
- package/scripts/doctor/fixes/registry-fix.js +56 -0
- package/scripts/doctor/index.js +212 -0
- package/scripts/health-check.js +8 -8
- package/src/autonomy/surface-criteria.js +226 -0
- package/src/context/bracket-tracker.js +44 -13
- package/src/context/domain-loader.js +22 -0
- package/src/context/engine.js +18 -7
- package/src/context/formatter.js +21 -1
- package/src/context/layers/l5-keywords.js +53 -0
- package/src/intelligence/context-status.js +20 -9
- package/src/intelligence/decision-engine.js +253 -0
- package/src/terminal/prompt-builder.js +341 -1
- package/src/terminal/run-agent.js +15 -0
- package/src/terminal/run-parallel.js +77 -5
- package/src/terminal/spawner.js +13 -0
- package/src/utils/feature-flags.js +106 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Tech Preset: Supabase Fullstack
|
|
2
|
+
# Auto-loaded by Architect agent when Supabase stack is detected
|
|
3
|
+
|
|
4
|
+
stack: supabase-fullstack
|
|
5
|
+
displayName: "Supabase Fullstack"
|
|
6
|
+
|
|
7
|
+
detection:
|
|
8
|
+
- supabase/config.toml
|
|
9
|
+
- .env.local containing SUPABASE_URL
|
|
10
|
+
|
|
11
|
+
conventions:
|
|
12
|
+
auth: "Supabase Auth with RLS policies for authorization"
|
|
13
|
+
database: "PostgreSQL with Row Level Security (RLS) on every table"
|
|
14
|
+
storage: "Supabase Storage for file uploads with bucket policies"
|
|
15
|
+
functions: "Supabase Edge Functions (Deno) for serverless logic"
|
|
16
|
+
realtime: "Supabase Realtime for live subscriptions"
|
|
17
|
+
|
|
18
|
+
structure:
|
|
19
|
+
- "supabase/ — Supabase project configuration"
|
|
20
|
+
- "supabase/migrations/ — SQL migration files"
|
|
21
|
+
- "supabase/functions/ — Edge Functions (Deno)"
|
|
22
|
+
- "supabase/seed.sql — Seed data"
|
|
23
|
+
- "src/lib/supabase.ts — Supabase client initialization"
|
|
24
|
+
|
|
25
|
+
rules:
|
|
26
|
+
- id: supabase-rls
|
|
27
|
+
text: "EVERY table MUST have RLS enabled. No exceptions. Create policies before exposing data."
|
|
28
|
+
priority: critical
|
|
29
|
+
- id: supabase-migrations
|
|
30
|
+
text: "All schema changes go through migration files. Never modify production DB directly."
|
|
31
|
+
priority: critical
|
|
32
|
+
- id: supabase-types
|
|
33
|
+
text: "Generate TypeScript types from database schema (supabase gen types)."
|
|
34
|
+
priority: high
|
|
35
|
+
- id: supabase-auth
|
|
36
|
+
text: "Use Supabase Auth helpers for SSR frameworks. Handle auth state server-side."
|
|
37
|
+
priority: high
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Brandbook Template
|
|
2
|
+
# Used by: UX Agent (Phase 0: Brand Identity)
|
|
3
|
+
# Language: English (Constitution Article VII)
|
|
4
|
+
|
|
5
|
+
template:
|
|
6
|
+
name: Brandbook
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
description: >
|
|
9
|
+
Brand identity foundation document. Defines voice, visual language,
|
|
10
|
+
typography rationale, color philosophy, spacing rhythm, and iconography
|
|
11
|
+
style. Must be completed BEFORE design tokens.
|
|
12
|
+
|
|
13
|
+
output_path: chati.dev/artifacts/4-UX/brandbook.md
|
|
14
|
+
|
|
15
|
+
sections:
|
|
16
|
+
- brand_voice
|
|
17
|
+
- visual_language
|
|
18
|
+
- typography_rationale
|
|
19
|
+
- color_philosophy
|
|
20
|
+
- spacing_rhythm
|
|
21
|
+
- iconography_style
|
|
22
|
+
|
|
23
|
+
brand_voice:
|
|
24
|
+
description: "Brand personality, tone, and communication style"
|
|
25
|
+
fields:
|
|
26
|
+
- name: personality_traits
|
|
27
|
+
type: array
|
|
28
|
+
description: "3-5 adjectives defining the brand personality"
|
|
29
|
+
- name: tone_spectrum
|
|
30
|
+
type: object
|
|
31
|
+
description: "Formal vs casual, technical vs approachable spectrum"
|
|
32
|
+
- name: writing_guidelines
|
|
33
|
+
type: array
|
|
34
|
+
description: "Dos and don'ts for brand voice"
|
|
35
|
+
- name: example_phrases
|
|
36
|
+
type: array
|
|
37
|
+
description: "Sample phrases demonstrating brand voice"
|
|
38
|
+
|
|
39
|
+
visual_language:
|
|
40
|
+
description: "Core visual principles and aesthetic direction"
|
|
41
|
+
fields:
|
|
42
|
+
- name: design_principles
|
|
43
|
+
type: array
|
|
44
|
+
description: "3-5 guiding principles for visual design"
|
|
45
|
+
- name: mood_keywords
|
|
46
|
+
type: array
|
|
47
|
+
description: "Keywords capturing the visual mood"
|
|
48
|
+
- name: inspiration_references
|
|
49
|
+
type: array
|
|
50
|
+
description: "Visual references and mood board elements"
|
|
51
|
+
|
|
52
|
+
typography_rationale:
|
|
53
|
+
description: "Font selection rationale and typographic hierarchy"
|
|
54
|
+
fields:
|
|
55
|
+
- name: primary_font
|
|
56
|
+
type: object
|
|
57
|
+
description: "Primary typeface and why it was chosen"
|
|
58
|
+
- name: secondary_font
|
|
59
|
+
type: object
|
|
60
|
+
description: "Secondary typeface for contrast"
|
|
61
|
+
- name: hierarchy_rules
|
|
62
|
+
type: array
|
|
63
|
+
description: "Heading, body, caption sizing rationale"
|
|
64
|
+
|
|
65
|
+
color_philosophy:
|
|
66
|
+
description: "Color palette rationale and emotional associations"
|
|
67
|
+
fields:
|
|
68
|
+
- name: primary_palette
|
|
69
|
+
type: array
|
|
70
|
+
description: "Primary colors with emotional rationale"
|
|
71
|
+
- name: semantic_mapping
|
|
72
|
+
type: object
|
|
73
|
+
description: "Color-to-meaning mapping (success, warning, error)"
|
|
74
|
+
- name: accessibility_notes
|
|
75
|
+
type: string
|
|
76
|
+
description: "WCAG contrast compliance notes"
|
|
77
|
+
|
|
78
|
+
spacing_rhythm:
|
|
79
|
+
description: "Spacing scale rationale and rhythm system"
|
|
80
|
+
fields:
|
|
81
|
+
- name: base_unit
|
|
82
|
+
type: number
|
|
83
|
+
description: "Base spacing unit in pixels"
|
|
84
|
+
- name: scale_rationale
|
|
85
|
+
type: string
|
|
86
|
+
description: "Why this scale was chosen (4px, 8px, etc.)"
|
|
87
|
+
- name: rhythm_rules
|
|
88
|
+
type: array
|
|
89
|
+
description: "Rules for consistent vertical and horizontal rhythm"
|
|
90
|
+
|
|
91
|
+
iconography_style:
|
|
92
|
+
description: "Icon style direction and usage philosophy"
|
|
93
|
+
fields:
|
|
94
|
+
- name: style_direction
|
|
95
|
+
type: string
|
|
96
|
+
description: "Outlined, filled, duotone, etc."
|
|
97
|
+
- name: stroke_weight
|
|
98
|
+
type: string
|
|
99
|
+
description: "Consistent stroke weight for icon set"
|
|
100
|
+
- name: metaphor_guidelines
|
|
101
|
+
type: array
|
|
102
|
+
description: "Guidelines for icon metaphor selection"
|
|
103
|
+
|
|
104
|
+
rules:
|
|
105
|
+
- id: brand-before-tokens
|
|
106
|
+
text: "Brandbook MUST be completed before design tokens are defined."
|
|
107
|
+
priority: critical
|
|
108
|
+
- id: brand-consistency
|
|
109
|
+
text: "All visual decisions must trace back to brandbook principles."
|
|
110
|
+
priority: high
|
|
111
|
+
- id: brand-voice-artifacts
|
|
112
|
+
text: "All user-facing text in artifacts must follow brand voice guidelines."
|
|
113
|
+
priority: high
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Component Spec Template
|
|
2
|
+
# Used by: UX Agent (Phase 2: Component Specifications)
|
|
3
|
+
# Language: English (Constitution Article VII)
|
|
4
|
+
|
|
5
|
+
template:
|
|
6
|
+
name: Component Spec
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
description: >
|
|
9
|
+
UI component specification document. Defines variants, states,
|
|
10
|
+
anatomy, props, accessibility, and responsive behavior.
|
|
11
|
+
|
|
12
|
+
output_path: chati.dev/artifacts/4-UX/components/
|
|
13
|
+
|
|
14
|
+
fields:
|
|
15
|
+
- name: id
|
|
16
|
+
type: string
|
|
17
|
+
description: "Unique component identifier (kebab-case)"
|
|
18
|
+
required: true
|
|
19
|
+
- name: name
|
|
20
|
+
type: string
|
|
21
|
+
description: "Human-readable component name"
|
|
22
|
+
required: true
|
|
23
|
+
- name: category
|
|
24
|
+
type: enum
|
|
25
|
+
values: [primitive, composite, layout, feedback, navigation, data-display, form]
|
|
26
|
+
description: "Component category for organization"
|
|
27
|
+
required: true
|
|
28
|
+
- name: description
|
|
29
|
+
type: string
|
|
30
|
+
description: "One-sentence description of the component purpose"
|
|
31
|
+
required: true
|
|
32
|
+
- name: variants
|
|
33
|
+
type: array
|
|
34
|
+
description: "Visual variants (primary, secondary, ghost, destructive, etc.)"
|
|
35
|
+
required: true
|
|
36
|
+
- name: sizes
|
|
37
|
+
type: array
|
|
38
|
+
description: "Size variants (xs, sm, md, lg, xl)"
|
|
39
|
+
required: true
|
|
40
|
+
- name: states
|
|
41
|
+
type: array
|
|
42
|
+
description: "Interactive states (default, hover, active, focus, disabled, loading, error)"
|
|
43
|
+
required: true
|
|
44
|
+
- name: anatomy
|
|
45
|
+
type: object
|
|
46
|
+
description: "Component parts and their structure"
|
|
47
|
+
required: true
|
|
48
|
+
- name: props
|
|
49
|
+
type: array
|
|
50
|
+
description: "Component API (props with types, defaults, descriptions)"
|
|
51
|
+
required: true
|
|
52
|
+
- name: tokens_used
|
|
53
|
+
type: array
|
|
54
|
+
description: "Design tokens consumed by this component"
|
|
55
|
+
required: true
|
|
56
|
+
- name: accessibility
|
|
57
|
+
type: object
|
|
58
|
+
description: "ARIA roles, keyboard navigation, screen reader behavior"
|
|
59
|
+
required: true
|
|
60
|
+
- name: responsive_behavior
|
|
61
|
+
type: object
|
|
62
|
+
description: "How component adapts across breakpoints"
|
|
63
|
+
required: false
|
|
64
|
+
|
|
65
|
+
rules:
|
|
66
|
+
- id: tokens-required
|
|
67
|
+
text: "Every visual property MUST reference a design token. No hardcoded values."
|
|
68
|
+
priority: critical
|
|
69
|
+
- id: accessibility-required
|
|
70
|
+
text: "Every component MUST have ARIA roles and keyboard navigation defined."
|
|
71
|
+
priority: critical
|
|
72
|
+
- id: state-coverage
|
|
73
|
+
text: "All interactive states must be specified including disabled and loading."
|
|
74
|
+
priority: high
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Design Token Template
|
|
2
|
+
# Used by: UX Agent (Phase 1: Design Tokens)
|
|
3
|
+
# Language: English (Constitution Article VII)
|
|
4
|
+
|
|
5
|
+
template:
|
|
6
|
+
name: Design Tokens
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
description: >
|
|
9
|
+
Structured design token system with 4 layers: primitive, semantic,
|
|
10
|
+
component, and utility. Derived from brandbook decisions.
|
|
11
|
+
|
|
12
|
+
output_path: chati.dev/artifacts/4-UX/design-tokens.md
|
|
13
|
+
|
|
14
|
+
layers:
|
|
15
|
+
- primitive
|
|
16
|
+
- semantic
|
|
17
|
+
- component
|
|
18
|
+
- utility
|
|
19
|
+
|
|
20
|
+
token_categories:
|
|
21
|
+
color:
|
|
22
|
+
primitive: "Raw color values (hex, hsl)"
|
|
23
|
+
semantic: "Mapped to meaning (primary, success, error)"
|
|
24
|
+
component: "Component-specific (button-bg, card-border)"
|
|
25
|
+
spacing:
|
|
26
|
+
primitive: "Base unit multiples (4, 8, 12, 16, 24, 32, 48, 64)"
|
|
27
|
+
semantic: "Mapped to usage (compact, default, relaxed)"
|
|
28
|
+
component: "Component-specific (card-padding, input-gap)"
|
|
29
|
+
typography:
|
|
30
|
+
primitive: "Font families, raw sizes, weights"
|
|
31
|
+
semantic: "Mapped to roles (heading, body, caption, label)"
|
|
32
|
+
component: "Component-specific (button-text, input-label)"
|
|
33
|
+
radius:
|
|
34
|
+
primitive: "Raw radius values (0, 2, 4, 8, 12, full)"
|
|
35
|
+
semantic: "Mapped to shape intent (sharp, soft, round, pill)"
|
|
36
|
+
shadow:
|
|
37
|
+
primitive: "Raw shadow definitions (offset, blur, spread, color)"
|
|
38
|
+
semantic: "Mapped to elevation (flat, raised, floating, overlay)"
|
|
39
|
+
motion:
|
|
40
|
+
primitive: "Duration and easing values"
|
|
41
|
+
semantic: "Mapped to intent (enter, exit, emphasis, feedback)"
|
|
42
|
+
|
|
43
|
+
rules:
|
|
44
|
+
- id: tokens-after-brandbook
|
|
45
|
+
text: "Design tokens MUST be derived from brandbook decisions."
|
|
46
|
+
priority: critical
|
|
47
|
+
- id: primitive-first
|
|
48
|
+
text: "Define primitive tokens before semantic. Semantic references primitive."
|
|
49
|
+
priority: high
|
|
50
|
+
- id: token-naming
|
|
51
|
+
text: "Use consistent naming: layer-category-variant (e.g., color-primary-500)."
|
|
52
|
+
priority: high
|
|
53
|
+
- id: accessibility-contrast
|
|
54
|
+
text: "All text color tokens must meet WCAG AA contrast ratio (4.5:1)."
|
|
55
|
+
priority: critical
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# Icon System Template
|
|
2
|
+
# Used by: UX Agent (Phase 0: Brand Identity / Iconography)
|
|
3
|
+
# Language: English (Constitution Article VII)
|
|
4
|
+
|
|
5
|
+
template:
|
|
6
|
+
name: Icon System
|
|
7
|
+
version: "1.0.0"
|
|
8
|
+
description: >
|
|
9
|
+
Icon library selection, sizing scale, usage guidelines, and inventory.
|
|
10
|
+
Aligned with brandbook iconography style.
|
|
11
|
+
|
|
12
|
+
output_path: chati.dev/artifacts/4-UX/icon-system.md
|
|
13
|
+
|
|
14
|
+
sections:
|
|
15
|
+
library_selection:
|
|
16
|
+
description: "Chosen icon library and rationale"
|
|
17
|
+
fields:
|
|
18
|
+
- name: library_name
|
|
19
|
+
type: string
|
|
20
|
+
description: "Selected icon library (e.g., Lucide, Phosphor, Heroicons)"
|
|
21
|
+
- name: rationale
|
|
22
|
+
type: string
|
|
23
|
+
description: "Why this library was chosen"
|
|
24
|
+
- name: style
|
|
25
|
+
type: enum
|
|
26
|
+
values: [outlined, filled, duotone, solid]
|
|
27
|
+
description: "Icon style variant"
|
|
28
|
+
- name: license
|
|
29
|
+
type: string
|
|
30
|
+
description: "Icon library license (MIT, Apache, etc.)"
|
|
31
|
+
|
|
32
|
+
sizing_scale:
|
|
33
|
+
description: "Icon size scale aligned with spacing tokens"
|
|
34
|
+
fields:
|
|
35
|
+
- name: xs
|
|
36
|
+
type: number
|
|
37
|
+
description: "Extra small icon size (12-14px)"
|
|
38
|
+
- name: sm
|
|
39
|
+
type: number
|
|
40
|
+
description: "Small icon size (16px)"
|
|
41
|
+
- name: md
|
|
42
|
+
type: number
|
|
43
|
+
description: "Medium icon size (20-24px)"
|
|
44
|
+
- name: lg
|
|
45
|
+
type: number
|
|
46
|
+
description: "Large icon size (28-32px)"
|
|
47
|
+
- name: xl
|
|
48
|
+
type: number
|
|
49
|
+
description: "Extra large icon size (40-48px)"
|
|
50
|
+
- name: xxl
|
|
51
|
+
type: number
|
|
52
|
+
description: "Display icon size (56-64px)"
|
|
53
|
+
|
|
54
|
+
usage_guidelines:
|
|
55
|
+
description: "Rules for consistent icon usage"
|
|
56
|
+
fields:
|
|
57
|
+
- name: with_text
|
|
58
|
+
type: string
|
|
59
|
+
description: "When to pair icons with text labels"
|
|
60
|
+
- name: standalone
|
|
61
|
+
type: string
|
|
62
|
+
description: "When icons can stand alone (with aria-label)"
|
|
63
|
+
- name: color_rules
|
|
64
|
+
type: string
|
|
65
|
+
description: "How icons inherit or override color tokens"
|
|
66
|
+
- name: touch_target
|
|
67
|
+
type: string
|
|
68
|
+
description: "Minimum touch target size (44x44px for mobile)"
|
|
69
|
+
|
|
70
|
+
icon_inventory:
|
|
71
|
+
description: "Catalog of icons used in the project"
|
|
72
|
+
fields:
|
|
73
|
+
- name: navigation
|
|
74
|
+
type: array
|
|
75
|
+
description: "Icons used in navigation elements"
|
|
76
|
+
- name: actions
|
|
77
|
+
type: array
|
|
78
|
+
description: "Icons for user actions (add, edit, delete, etc.)"
|
|
79
|
+
- name: status
|
|
80
|
+
type: array
|
|
81
|
+
description: "Icons for status indicators (success, warning, error, info)"
|
|
82
|
+
- name: content
|
|
83
|
+
type: array
|
|
84
|
+
description: "Icons for content types (file, folder, image, etc.)"
|
|
85
|
+
|
|
86
|
+
rules:
|
|
87
|
+
- id: icon-library-consistency
|
|
88
|
+
text: "Use ONE icon library per project. Do not mix libraries."
|
|
89
|
+
priority: critical
|
|
90
|
+
- id: icon-accessibility
|
|
91
|
+
text: "Standalone icons MUST have aria-label. Decorative icons use aria-hidden."
|
|
92
|
+
priority: critical
|
|
93
|
+
- id: icon-sizing-alignment
|
|
94
|
+
text: "Icon sizes must align with the spacing token scale from design tokens."
|
|
95
|
+
priority: high
|
package/package.json
CHANGED
|
@@ -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
|
+
}
|