chati-dev 4.2.2 → 4.3.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 +80 -53
- package/bin/chati.js +150 -5
- package/framework/agents/build/dev.md +509 -76
- package/framework/agents/deploy/devops.md +40 -25
- package/framework/agents/discover/brief.md +156 -22
- package/framework/agents/discover/brownfield-wu.md +24 -14
- package/framework/agents/discover/greenfield-wu.md +100 -14
- package/framework/agents/plan/architect-data-engineer.md +6 -6
- package/framework/agents/plan/architect-system.md +46 -12
- package/framework/agents/plan/architect.md +40 -20
- package/framework/agents/plan/detail.md +36 -24
- package/framework/agents/plan/phases.md +36 -26
- package/framework/agents/plan/tasks.md +114 -33
- package/framework/agents/plan/ux-brand-architect.md +240 -8
- package/framework/agents/plan/ux-component-engineer.md +28 -13
- package/framework/agents/plan/ux-researcher.md +7 -6
- package/framework/agents/plan/ux.md +55 -22
- package/framework/agents/quality/qa-implementation.md +143 -74
- package/framework/agents/quality/qa-planning.md +115 -42
- package/framework/agents/quality/qa-visual.md +439 -0
- package/framework/agents/shared/visualizer.md +128 -0
- package/framework/config.yaml +7 -6
- package/framework/constitution.md +127 -44
- package/framework/context/governance.md +12 -7
- package/framework/context/quality.md +6 -5
- package/framework/context/root.md +6 -6
- package/framework/data/entity-registry.yaml +377 -4
- package/framework/data/model-limits.json +19 -0
- package/framework/domains/agents/qa-visual.yaml +74 -0
- package/framework/domains/constitution.yaml +46 -2
- package/framework/domains/workflows/greenfield-fullstack.yaml +2 -2
- package/framework/hooks/advance-trigger.js +131 -0
- package/framework/hooks/brief-validator.js +83 -0
- package/framework/hooks/constitution-guard.js +24 -5
- package/framework/hooks/license-guard.js +62 -27
- package/framework/hooks/mode-governance.js +13 -2
- package/framework/hooks/model-governance.js +1 -0
- package/framework/hooks/post-dev.js +207 -0
- package/framework/hooks/prism-engine.js +274 -105
- package/framework/hooks/reasoning-escalator.js +371 -0
- package/framework/hooks/reference-trigger.js +117 -0
- package/framework/hooks/session-digest.js +50 -1
- package/framework/hooks/settings.json +32 -1
- package/framework/hooks/style-guard.js +25 -6
- package/framework/hooks/team-quality-gate.js +19 -12
- package/framework/hooks/undercover-guard.js +4 -2
- package/framework/i18n/en.yaml +3 -3
- package/framework/i18n/es.yaml +3 -3
- package/framework/i18n/fr.yaml +3 -3
- package/framework/i18n/pt.yaml +3 -3
- package/framework/intelligence/context-engine.md +4 -5
- package/framework/intelligence/decision-engine.md +1 -1
- package/framework/intelligence/hooks-performance.md +3 -3
- package/framework/migrations/v1.0-to-v1.1.yaml +1 -1
- package/framework/migrations/v1.4-to-v2.0.yaml +11 -11
- package/framework/migrations/v4.0-to-v4.1.yaml +2 -2
- package/framework/migrations/v4.2-to-v4.3.yaml +29 -0
- package/framework/orchestrator/chati-router.js +387 -0
- package/framework/orchestrator/chati-update.md +40 -40
- package/framework/orchestrator/chati.md +294 -94
- package/framework/scaffold/motion-premium/README.md +89 -0
- package/framework/scaffold/motion-premium/app/globals.css.template +400 -0
- package/framework/scaffold/motion-premium/app/layout.tsx.template +110 -0
- package/framework/scaffold/motion-premium/components/animation/BackgroundCrossfadeProvider.tsx.template +170 -0
- package/framework/scaffold/motion-premium/components/animation/LenisProvider.tsx.template +49 -0
- package/framework/scaffold/motion-premium/components/animation/PageTransitionWrapper.tsx.template +83 -0
- package/framework/scaffold/motion-premium/components/animation/Preloader.tsx.template +171 -0
- package/framework/scaffold/motion-premium/components/ui/Container.tsx.template +69 -0
- package/framework/scaffold/motion-premium/components/ui/PageSection.tsx.template +74 -0
- package/framework/scaffold/motion-premium/lib/animations/gsap.ts.template +112 -0
- package/framework/scaffold/motion-premium/lib/animations/refreshCoordinator.ts.template +75 -0
- package/framework/scaffold/motion-premium/lib/animations/tokens.ts.template +119 -0
- package/framework/scaffold/motion-premium/lib/animations/useGsapContext.ts.template +92 -0
- package/framework/scaffold/motion-premium/lib/animations/useScrollSnapStepper.ts.template +265 -0
- package/framework/scaffold/motion-premium/lib/animations/useSmoothScroll.ts.template +67 -0
- package/framework/scaffold/motion-premium/lib/brand.ts.template +43 -0
- package/framework/scaffold/motion-premium/scaffold.yaml +174 -0
- package/framework/scaffold/motion-premium-3d/README.md +80 -0
- package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoCanvas.tsx.template +81 -0
- package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/ScrollDemoClient.tsx.template +75 -0
- package/framework/scaffold/motion-premium-3d/app/(3d)/scroll-demo/page.tsx.template +26 -0
- package/framework/scaffold/motion-premium-3d/components/3d/CameraRig.tsx.template +100 -0
- package/framework/scaffold/motion-premium-3d/components/3d/CanvasProvider.tsx.template +85 -0
- package/framework/scaffold/motion-premium-3d/components/3d/InvalidateOnScroll.tsx.template +51 -0
- package/framework/scaffold/motion-premium-3d/components/3d/MeshCrossfade.tsx.template +79 -0
- package/framework/scaffold/motion-premium-3d/components/3d/ScrollCrossfade.tsx.template +88 -0
- package/framework/scaffold/motion-premium-3d/components/3d/ScrollScene.tsx.template +121 -0
- package/framework/scaffold/motion-premium-3d/components/webgl/SceneFallback.tsx.template +65 -0
- package/framework/scaffold/motion-premium-3d/components/webgl/WebGLContext.tsx.template +68 -0
- package/framework/scaffold/motion-premium-3d/lib/webgl/detect.ts.template +69 -0
- package/framework/scaffold/motion-premium-3d/scaffold.yaml +133 -0
- package/framework/schemas/session.schema.json +109 -21
- package/framework/scripts/reference-capture.js +430 -0
- package/framework/scripts/visual-qa.js +674 -0
- package/framework/tasks/orchestrator-handoff.md +1 -1
- package/framework/tasks/orchestrator-resume.md +1 -1
- package/framework/tasks/orchestrator-route.md +1 -1
- package/framework/tasks/orchestrator-status.md +3 -3
- package/framework/tasks/qa-planning-gate-define.md +1 -1
- package/framework/templates/brandbook-html-tmpl.md +1 -1
- package/framework/templates/brandbook-tmpl.yaml +1 -1
- package/framework/templates/component-spec-tmpl.yaml +1 -1
- package/framework/templates/design-token-tmpl.yaml +1 -1
- package/framework/templates/icon-system-tmpl.yaml +1 -1
- package/framework/templates/team-planning-tasks.yaml +6 -5
- package/framework/workflows/brownfield-discovery.yaml +2 -2
- package/framework/workflows/brownfield-fullstack.yaml +15 -11
- package/framework/workflows/brownfield-service.yaml +14 -10
- package/framework/workflows/brownfield-ui.yaml +15 -11
- package/framework/workflows/greenfield-fullstack.yaml +16 -13
- package/framework/workflows/quick-flow.yaml +3 -3
- package/framework/workflows/standard-flow.yaml +12 -9
- package/package.json +10 -5
- package/src/autonomy/autonomous-gate.js +1 -0
- package/src/autonomy/build-state.js +1 -2
- package/src/autonomy/progress-reporter.js +1 -1
- package/src/config/agent-customizer.js +11 -3
- package/src/config/claude-settings-generator.js +27 -7
- package/src/config/context-file-generator.js +41 -21
- package/src/config/framework-adapter.js +1 -0
- package/src/config/gemini-hooks-generator.js +19 -7
- package/src/config/mcp-configs.js +1 -0
- package/src/context/layers/l1-global.js +2 -1
- package/src/dashboard/data-reader.js +4 -3
- package/src/dashboard/layout.js +2 -1
- package/src/decision/analyzer.js +6 -30
- package/src/decision/engine.js +4 -28
- package/src/decision/registry-healer.js +3 -2
- package/src/decision/registry-updater.js +23 -14
- package/src/extensions/loader.js +2 -8
- package/src/gates/g1-planning-complete.js +2 -1
- package/src/gates/g2-qa-planning.js +2 -1
- package/src/gates/g3-implementation.js +2 -1
- package/src/gates/g4-qa-implementation.js +3 -2
- package/src/gates/g5-deploy-ready.js +2 -1
- package/src/health/engine.js +4 -3
- package/src/installer/core.js +422 -81
- package/src/installer/preflight.js +131 -0
- package/src/installer/provider-overlay.js +3 -3
- package/src/installer/scaffold-applier.js +358 -0
- package/src/installer/templates.js +46 -29
- package/src/installer/validator.js +17 -12
- package/src/intelligence/registry-manager.js +22 -29
- package/src/intelligence/timeline.js +11 -6
- package/src/license/commands.js +1 -1
- package/src/license/wait.js +102 -0
- package/src/memory/agent-memory.js +81 -0
- package/src/memory/dream.js +32 -1
- package/src/merger/replace-merger.js +28 -15
- package/src/orchestrator/agent-selector.js +2 -1
- package/src/orchestrator/cli.js +1869 -71
- package/src/orchestrator/doctor.js +270 -0
- package/src/orchestrator/handoff-engine.js +4 -3
- package/src/orchestrator/index.js +2 -0
- package/src/orchestrator/pipeline-manager.js +306 -15
- package/src/orchestrator/session-manager.js +331 -6
- package/src/tasks/handoff.js +3 -2
- package/src/telemetry/config.js +4 -3
- package/src/telemetry/schema.js +1 -0
- package/src/terminal/collector.js +3 -2
- package/src/terminal/index.js +1 -2
- package/src/terminal/isolation.js +52 -18
- package/src/terminal/prompt-builder.js +42 -25
- package/src/terminal/run-parallel.js +1 -1
- package/src/terminal/run-team.js +3 -3
- package/src/terminal/team-task-list.js +43 -4
- package/src/upgrade/backup.js +3 -2
- package/src/upgrade/checker.js +3 -2
- package/src/upgrade/migrator.js +65 -7
- package/src/upgrade/tracked-files-detector.js +86 -0
- package/src/upgrade/user-messages.js +94 -0
- package/src/utils/config-parser.js +2 -1
- package/src/utils/feature-flags.js +2 -1
- package/src/utils/flatten-entities.js +69 -0
- package/src/utils/framework-dir.js +16 -0
- package/src/utils/model-id.js +85 -0
- package/src/utils/provider-limits.js +84 -23
- package/src/utils/schema-validator.js +1 -1
- package/src/wizard/i18n.js +5 -4
- package/src/wizard/index.js +14 -0
- package/assets/logo - c/303/263pia.png +0 -0
- package/assets/logo.svg +0 -42
- package/assets/logo2.png +0 -0
- package/assets/social-preview.png +0 -0
- package/scripts/bundle-framework.js +0 -69
- package/scripts/changelog-generator.js +0 -222
- package/scripts/codebase-mapper.js +0 -728
- package/scripts/commit-message-generator.js +0 -167
- package/scripts/coverage-analyzer.js +0 -260
- package/scripts/dependency-analyzer.js +0 -280
- package/scripts/doctor/checks/agents.js +0 -77
- package/scripts/doctor/checks/constitution.js +0 -41
- package/scripts/doctor/checks/domain-alignment.js +0 -58
- package/scripts/doctor/checks/prism-layers.js +0 -84
- package/scripts/doctor/checks/registry.js +0 -55
- package/scripts/doctor/checks/schemas.js +0 -61
- package/scripts/doctor/fixes/reference-fix.js +0 -100
- package/scripts/doctor/fixes/registry-fix.js +0 -56
- package/scripts/doctor/index.js +0 -212
- package/scripts/framework-analyzer.js +0 -308
- package/scripts/generate-constitution-domain.js +0 -253
- package/scripts/generate-signing-key.js +0 -33
- package/scripts/health-check.js +0 -481
- package/scripts/ide-sync.js +0 -326
- package/scripts/performance-analyzer.js +0 -325
- package/scripts/plan-tracker.js +0 -278
- package/scripts/populate-entity-registry.js +0 -481
- package/scripts/pr-review.js +0 -317
- package/scripts/rollback-manager.js +0 -310
- package/scripts/semantic-lint.js +0 -328
- package/scripts/sign-manifest.js +0 -53
- package/scripts/stuck-detector.js +0 -343
- package/scripts/test-quality-assessment.js +0 -257
- package/scripts/validate-agents.js +0 -368
- package/scripts/validate-package.js +0 -505
- package/scripts/validate-tasks.js +0 -465
- package/src/autonomy/worktree-manager.js +0 -250
- package/src/intelligence/decision-engine.js +0 -256
- package/src/intelligence/document-sharder.js +0 -221
- package/src/intelligence/elicitation.js +0 -265
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Package Completeness Validator
|
|
5
|
-
*
|
|
6
|
-
* Validates that the npm package has all required files before publishing.
|
|
7
|
-
* Run as part of prepublishOnly (after bundle, before sign-manifest).
|
|
8
|
-
*
|
|
9
|
-
* Exports:
|
|
10
|
-
* validatePackage(packageRoot) → { errors, warnings, checks, passed }
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
14
|
-
import { join, dirname } from 'path';
|
|
15
|
-
import { fileURLToPath } from 'url';
|
|
16
|
-
|
|
17
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Expected directories in framework/ (must match bundle-framework.js)
|
|
21
|
-
*/
|
|
22
|
-
const EXPECTED_BUNDLE_DIRS = [
|
|
23
|
-
'orchestrator',
|
|
24
|
-
'agents/discover', 'agents/plan', 'agents/quality', 'agents/build', 'agents/deploy',
|
|
25
|
-
'templates', 'workflows', 'quality-gates',
|
|
26
|
-
'schemas', 'frameworks', 'intelligence', 'patterns',
|
|
27
|
-
'hooks', 'domains',
|
|
28
|
-
'i18n', 'migrations', 'data',
|
|
29
|
-
'tasks', 'context',
|
|
30
|
-
];
|
|
31
|
-
|
|
32
|
-
const EXPECTED_ROOT_FILES = ['constitution.md', 'config.yaml'];
|
|
33
|
-
|
|
34
|
-
const SENSITIVE_FILES = ['.signing-key.pem', '.env', '.env.local', '.env.production'];
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Validate that framework/ directory exists.
|
|
38
|
-
*/
|
|
39
|
-
function checkFrameworkExists(packageRoot, results) {
|
|
40
|
-
results.checks++;
|
|
41
|
-
const frameworkDir = join(packageRoot, 'framework');
|
|
42
|
-
if (existsSync(frameworkDir)) {
|
|
43
|
-
results.passed++;
|
|
44
|
-
} else {
|
|
45
|
-
results.errors.push('framework/ directory not found. Run `npm run bundle` first.');
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Validate all expected bundle directories exist.
|
|
51
|
-
*/
|
|
52
|
-
function checkBundledDirs(packageRoot, results) {
|
|
53
|
-
results.checks++;
|
|
54
|
-
const frameworkDir = join(packageRoot, 'framework');
|
|
55
|
-
if (!existsSync(frameworkDir)) return;
|
|
56
|
-
|
|
57
|
-
const missing = [];
|
|
58
|
-
for (const dir of EXPECTED_BUNDLE_DIRS) {
|
|
59
|
-
if (!existsSync(join(frameworkDir, dir))) {
|
|
60
|
-
missing.push(dir);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (missing.length === 0) {
|
|
65
|
-
results.passed++;
|
|
66
|
-
} else {
|
|
67
|
-
results.errors.push(`Missing framework directories: ${missing.join(', ')}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Validate root framework files exist.
|
|
73
|
-
*/
|
|
74
|
-
function checkRootFiles(packageRoot, results) {
|
|
75
|
-
results.checks++;
|
|
76
|
-
const frameworkDir = join(packageRoot, 'framework');
|
|
77
|
-
if (!existsSync(frameworkDir)) return;
|
|
78
|
-
|
|
79
|
-
const missing = [];
|
|
80
|
-
for (const file of EXPECTED_ROOT_FILES) {
|
|
81
|
-
if (!existsSync(join(frameworkDir, file))) {
|
|
82
|
-
missing.push(file);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (missing.length === 0) {
|
|
87
|
-
results.passed++;
|
|
88
|
-
} else {
|
|
89
|
-
results.errors.push(`Missing framework root files: ${missing.join(', ')}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Check that no sensitive files leaked into the bundle.
|
|
95
|
-
*/
|
|
96
|
-
function checkNoSensitiveFiles(packageRoot, results) {
|
|
97
|
-
results.checks++;
|
|
98
|
-
const found = [];
|
|
99
|
-
|
|
100
|
-
// Only check inside publishable directories (bin/, src/, assets/, framework/, scripts/)
|
|
101
|
-
// Root-level files like .signing-key.pem are excluded by package.json "files" field
|
|
102
|
-
const publishableDirs = ['framework', 'src', 'scripts', 'bin', 'assets'];
|
|
103
|
-
for (const file of SENSITIVE_FILES) {
|
|
104
|
-
for (const dir of publishableDirs) {
|
|
105
|
-
if (existsSync(join(packageRoot, dir, file))) {
|
|
106
|
-
found.push(`${dir}/${file}`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (found.length === 0) {
|
|
112
|
-
results.passed++;
|
|
113
|
-
} else {
|
|
114
|
-
results.errors.push(`Sensitive files found in package: ${found.join(', ')}`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Validate that all package.json exports resolve to existing files.
|
|
120
|
-
*/
|
|
121
|
-
function checkExportsResolve(packageRoot, results) {
|
|
122
|
-
results.checks++;
|
|
123
|
-
const pkgPath = join(packageRoot, 'package.json');
|
|
124
|
-
if (!existsSync(pkgPath)) return;
|
|
125
|
-
|
|
126
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
127
|
-
const exports = pkg.exports || {};
|
|
128
|
-
const missing = [];
|
|
129
|
-
|
|
130
|
-
for (const [key, value] of Object.entries(exports)) {
|
|
131
|
-
const filePath = join(packageRoot, value);
|
|
132
|
-
if (!existsSync(filePath)) {
|
|
133
|
-
missing.push(`${key} → ${value}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (missing.length === 0) {
|
|
138
|
-
results.passed++;
|
|
139
|
-
} else {
|
|
140
|
-
results.errors.push(`Unresolved exports: ${missing.join(', ')}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Validate that bin entry exists.
|
|
146
|
-
*/
|
|
147
|
-
function checkBinExists(packageRoot, results) {
|
|
148
|
-
results.checks++;
|
|
149
|
-
const pkgPath = join(packageRoot, 'package.json');
|
|
150
|
-
if (!existsSync(pkgPath)) return;
|
|
151
|
-
|
|
152
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
153
|
-
const bin = pkg.bin || {};
|
|
154
|
-
const missing = [];
|
|
155
|
-
|
|
156
|
-
for (const [name, path] of Object.entries(bin)) {
|
|
157
|
-
if (!existsSync(join(packageRoot, path))) {
|
|
158
|
-
missing.push(`${name} → ${path}`);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (missing.length === 0) {
|
|
163
|
-
results.passed++;
|
|
164
|
-
} else {
|
|
165
|
-
results.errors.push(`Missing bin entries: ${missing.join(', ')}`);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Validate that every hook referenced in hooks/settings.json exists on disk.
|
|
171
|
-
* Prevents shipping a bundle where settings.json references a hook file that
|
|
172
|
-
* was never copied.
|
|
173
|
-
*/
|
|
174
|
-
function checkHooksParity(packageRoot, results) {
|
|
175
|
-
results.checks++;
|
|
176
|
-
const settingsPath = join(packageRoot, 'framework', 'hooks', 'settings.json');
|
|
177
|
-
if (!existsSync(settingsPath)) {
|
|
178
|
-
results.warnings.push('framework/hooks/settings.json not found — skipping hooks parity check.');
|
|
179
|
-
results.passed++;
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
try {
|
|
184
|
-
const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
|
|
185
|
-
const missing = new Set();
|
|
186
|
-
const hooksDir = join(packageRoot, 'framework', 'hooks');
|
|
187
|
-
|
|
188
|
-
// Walk all hook events and collect referenced .js files
|
|
189
|
-
for (const event of Object.keys(settings.hooks || {})) {
|
|
190
|
-
for (const matcher of settings.hooks[event] || []) {
|
|
191
|
-
for (const hook of matcher.hooks || []) {
|
|
192
|
-
if (hook.type === 'command' && typeof hook.command === 'string') {
|
|
193
|
-
// command format: "node chati.dev/hooks/foo.js"
|
|
194
|
-
const match = hook.command.match(/hooks\/([^\s]+\.js)/);
|
|
195
|
-
if (match) {
|
|
196
|
-
const hookFile = match[1];
|
|
197
|
-
if (!existsSync(join(hooksDir, hookFile))) {
|
|
198
|
-
missing.add(hookFile);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
if (missing.size === 0) {
|
|
207
|
-
results.passed++;
|
|
208
|
-
} else {
|
|
209
|
-
results.errors.push(`Hooks referenced in settings.json but missing from framework/hooks/: ${[...missing].join(', ')}`);
|
|
210
|
-
}
|
|
211
|
-
} catch (err) {
|
|
212
|
-
results.errors.push(`Failed to parse hooks/settings.json: ${err.message}`);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* Parity check: every file in source chati.dev/ (that belongs to a copied
|
|
218
|
-
* directory) must exist in framework/. Catches bundling bugs where new files
|
|
219
|
-
* are added to the source but not included in the bundle.
|
|
220
|
-
*
|
|
221
|
-
* Only runs when the monorepo source is available (skipped inside the
|
|
222
|
-
* published package since the source is not shipped).
|
|
223
|
-
*/
|
|
224
|
-
function checkSourceBundleParity(packageRoot, results) {
|
|
225
|
-
results.checks++;
|
|
226
|
-
const monorepoSource = join(packageRoot, '..', '..', 'chati.dev');
|
|
227
|
-
const bundleDir = join(packageRoot, 'framework');
|
|
228
|
-
|
|
229
|
-
if (!existsSync(monorepoSource)) {
|
|
230
|
-
// Running inside the published package — skip (nothing to compare against)
|
|
231
|
-
results.passed++;
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
// Collect all files in source, grouped by the top-level directory
|
|
236
|
-
const missing = [];
|
|
237
|
-
const COPIED_DIRS = [
|
|
238
|
-
'orchestrator', 'agents', 'templates', 'workflows', 'quality-gates',
|
|
239
|
-
'schemas', 'frameworks', 'intelligence', 'patterns', 'hooks',
|
|
240
|
-
'domains', 'i18n', 'migrations', 'data', 'context', 'tasks', 'presets',
|
|
241
|
-
];
|
|
242
|
-
const COPIED_ROOT_FILES = ['constitution.md'];
|
|
243
|
-
|
|
244
|
-
function walk(srcDir, relBase) {
|
|
245
|
-
if (!existsSync(srcDir)) return;
|
|
246
|
-
for (const entry of readdirSync(srcDir, { withFileTypes: true })) {
|
|
247
|
-
const srcPath = join(srcDir, entry.name);
|
|
248
|
-
const relPath = relBase ? `${relBase}/${entry.name}` : entry.name;
|
|
249
|
-
if (entry.isDirectory()) {
|
|
250
|
-
walk(srcPath, relPath);
|
|
251
|
-
} else if (entry.isFile()) {
|
|
252
|
-
const bundlePath = join(bundleDir, relPath);
|
|
253
|
-
if (!existsSync(bundlePath)) missing.push(relPath);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
for (const file of COPIED_ROOT_FILES) {
|
|
259
|
-
const src = join(monorepoSource, file);
|
|
260
|
-
const bundled = join(bundleDir, file);
|
|
261
|
-
if (existsSync(src) && !existsSync(bundled)) missing.push(file);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
for (const dir of COPIED_DIRS) {
|
|
265
|
-
walk(join(monorepoSource, dir), dir);
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
if (missing.length === 0) {
|
|
269
|
-
results.passed++;
|
|
270
|
-
} else {
|
|
271
|
-
const sample = missing.slice(0, 10).join(', ');
|
|
272
|
-
const more = missing.length > 10 ? ` (+ ${missing.length - 10} more)` : '';
|
|
273
|
-
results.errors.push(`Files in source chati.dev/ missing from framework/ bundle: ${sample}${more}`);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Compare entity count in registry vs actual files in framework/.
|
|
279
|
-
*/
|
|
280
|
-
function checkEntityCount(packageRoot, results) {
|
|
281
|
-
results.checks++;
|
|
282
|
-
const registryPath = join(packageRoot, 'framework', 'data', 'entity-registry.yaml');
|
|
283
|
-
if (!existsSync(registryPath)) {
|
|
284
|
-
results.warnings.push('Entity registry not found in framework/data/ — skipping count check.');
|
|
285
|
-
results.passed++;
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
// Count files in framework/ recursively (excluding manifest.json, manifest.sig)
|
|
290
|
-
let fileCount = 0;
|
|
291
|
-
function countFiles(dir) {
|
|
292
|
-
try {
|
|
293
|
-
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
294
|
-
if (entry.isDirectory()) {
|
|
295
|
-
countFiles(join(dir, entry.name));
|
|
296
|
-
} else if (entry.name !== 'manifest.json' && entry.name !== 'manifest.sig') {
|
|
297
|
-
fileCount++;
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
} catch { /* skip unreadable dirs */ }
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
countFiles(join(packageRoot, 'framework'));
|
|
304
|
-
|
|
305
|
-
if (fileCount > 0) {
|
|
306
|
-
results.passed++;
|
|
307
|
-
} else {
|
|
308
|
-
results.errors.push('Framework directory is empty (0 files).');
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
/**
|
|
313
|
-
* v4.2.2: Validate that generateClaudeSettings() produces a settings.json
|
|
314
|
-
* that wires all 10 chati hooks via the canonical hookSpecificOutput schema
|
|
315
|
-
* — at the right events and with paths that resolve to existing files.
|
|
316
|
-
*
|
|
317
|
-
* This catches the v4.2.0/v4.2.1 catastrophe (hooks dormant because the
|
|
318
|
-
* installer never wrote .claude/settings.json) at validation time.
|
|
319
|
-
*/
|
|
320
|
-
async function checkClaudeHooksWired(packageRoot, results) {
|
|
321
|
-
results.checks++;
|
|
322
|
-
try {
|
|
323
|
-
const genPath = join(packageRoot, 'src', 'config', 'claude-settings-generator.js');
|
|
324
|
-
if (!existsSync(genPath)) {
|
|
325
|
-
results.errors.push('claude-settings-generator.js missing — Claude hooks would not be wired in installs.');
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
const mod = await import(genPath);
|
|
330
|
-
if (typeof mod.generateClaudeSettings !== 'function') {
|
|
331
|
-
results.errors.push('claude-settings-generator.js does not export generateClaudeSettings.');
|
|
332
|
-
return;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const settings = JSON.parse(mod.generateClaudeSettings());
|
|
336
|
-
|
|
337
|
-
// Verify required events
|
|
338
|
-
const requiredEvents = ['UserPromptSubmit', 'PreToolUse', 'PreCompact'];
|
|
339
|
-
const missingEvents = requiredEvents.filter(e => !settings.hooks?.[e]);
|
|
340
|
-
if (missingEvents.length > 0) {
|
|
341
|
-
results.errors.push(`generateClaudeSettings missing events: ${missingEvents.join(', ')}`);
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Collect all referenced hook commands
|
|
346
|
-
const allCommands = [];
|
|
347
|
-
for (const event of Object.values(settings.hooks)) {
|
|
348
|
-
for (const group of event) {
|
|
349
|
-
for (const hook of group.hooks || []) {
|
|
350
|
-
allCommands.push(hook.command || '');
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
// Verify all 10 chati hooks are referenced
|
|
356
|
-
const requiredHooks = [
|
|
357
|
-
'license-guard', 'prism-engine', 'model-governance',
|
|
358
|
-
'read-protection', 'constitution-guard', 'mode-governance',
|
|
359
|
-
'style-guard', 'undercover-guard', 'team-quality-gate', 'session-digest',
|
|
360
|
-
];
|
|
361
|
-
const missingHooks = requiredHooks.filter(h => !allCommands.some(c => c.includes(h)));
|
|
362
|
-
if (missingHooks.length > 0) {
|
|
363
|
-
results.errors.push(`generateClaudeSettings missing hook references: ${missingHooks.join(', ')}`);
|
|
364
|
-
return;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Verify each referenced hook file exists in framework/hooks/
|
|
368
|
-
const hooksDir = join(packageRoot, 'framework', 'hooks');
|
|
369
|
-
if (existsSync(hooksDir)) {
|
|
370
|
-
const missingFiles = [];
|
|
371
|
-
for (const cmd of allCommands) {
|
|
372
|
-
const match = cmd.match(/hooks\/([^\s]+\.js)/);
|
|
373
|
-
if (match && !existsSync(join(hooksDir, match[1]))) {
|
|
374
|
-
missingFiles.push(match[1]);
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
if (missingFiles.length > 0) {
|
|
378
|
-
results.errors.push(`Claude settings reference hook files missing from bundle: ${missingFiles.join(', ')}`);
|
|
379
|
-
return;
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
results.passed++;
|
|
384
|
-
} catch (err) {
|
|
385
|
-
results.errors.push(`checkClaudeHooksWired failed: ${err.message}`);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* v4.2.2: Verify license-guard is wired for ALL three providers.
|
|
391
|
-
* Without this, an expired trial keeps working forever — exactly the bug
|
|
392
|
-
* the user flagged in v4.2.1 (they could see chati.dev with no license check).
|
|
393
|
-
*/
|
|
394
|
-
async function checkLicenseEntryPointAcrossProviders(packageRoot, results) {
|
|
395
|
-
results.checks++;
|
|
396
|
-
try {
|
|
397
|
-
const missing = [];
|
|
398
|
-
|
|
399
|
-
// Claude — settings.json must include license-guard in UserPromptSubmit
|
|
400
|
-
const genPath = join(packageRoot, 'src', 'config', 'claude-settings-generator.js');
|
|
401
|
-
if (existsSync(genPath)) {
|
|
402
|
-
const mod = await import(genPath);
|
|
403
|
-
const settings = JSON.parse(mod.generateClaudeSettings());
|
|
404
|
-
const ups = settings.hooks?.UserPromptSubmit || [];
|
|
405
|
-
const claudeCmds = ups.flatMap(g => (g.hooks || []).map(h => h.command || ''));
|
|
406
|
-
if (!claudeCmds.some(c => c.includes('license-guard'))) {
|
|
407
|
-
missing.push('Claude (UserPromptSubmit)');
|
|
408
|
-
}
|
|
409
|
-
} else {
|
|
410
|
-
missing.push('Claude (generator file missing)');
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// Gemini — HOOK_MAP must include license-guard at BeforeModel
|
|
414
|
-
const geminiPath = join(packageRoot, 'src', 'config', 'gemini-hooks-generator.js');
|
|
415
|
-
if (existsSync(geminiPath)) {
|
|
416
|
-
const mod = await import(geminiPath);
|
|
417
|
-
if (!mod.HOOK_MAP?.['license-guard'] || mod.HOOK_MAP['license-guard'].event !== 'BeforeModel') {
|
|
418
|
-
missing.push('Gemini (HOOK_MAP missing license-guard at BeforeModel)');
|
|
419
|
-
}
|
|
420
|
-
} else {
|
|
421
|
-
missing.push('Gemini (generator file missing)');
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// Codex — installer/core.js must write .codex/hooks.json with license-guard
|
|
425
|
-
const corePath = join(packageRoot, 'src', 'installer', 'core.js');
|
|
426
|
-
if (existsSync(corePath)) {
|
|
427
|
-
const coreContent = readFileSync(corePath, 'utf-8');
|
|
428
|
-
if (!coreContent.includes('.codex/hooks.json') || !coreContent.includes("license-guard")) {
|
|
429
|
-
missing.push('Codex (.codex/hooks.json not wired with license-guard in core.js)');
|
|
430
|
-
}
|
|
431
|
-
} else {
|
|
432
|
-
missing.push('Codex (core.js missing)');
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (missing.length > 0) {
|
|
436
|
-
results.errors.push(`License enforcement gaps: ${missing.join('; ')}`);
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
results.passed++;
|
|
441
|
-
} catch (err) {
|
|
442
|
-
results.errors.push(`checkLicenseEntryPointAcrossProviders failed: ${err.message}`);
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
/**
|
|
447
|
-
* Validate the npm package completeness.
|
|
448
|
-
* @param {string} packageRoot - Root of the package (packages/chati-dev/)
|
|
449
|
-
* @returns {Promise<{ errors: string[], warnings: string[], checks: number, passed: number }>}
|
|
450
|
-
*/
|
|
451
|
-
export async function validatePackage(packageRoot) {
|
|
452
|
-
const results = { errors: [], warnings: [], checks: 0, passed: 0 };
|
|
453
|
-
|
|
454
|
-
checkFrameworkExists(packageRoot, results);
|
|
455
|
-
checkBundledDirs(packageRoot, results);
|
|
456
|
-
checkRootFiles(packageRoot, results);
|
|
457
|
-
checkNoSensitiveFiles(packageRoot, results);
|
|
458
|
-
checkExportsResolve(packageRoot, results);
|
|
459
|
-
checkBinExists(packageRoot, results);
|
|
460
|
-
checkEntityCount(packageRoot, results);
|
|
461
|
-
checkHooksParity(packageRoot, results);
|
|
462
|
-
checkSourceBundleParity(packageRoot, results);
|
|
463
|
-
await checkClaudeHooksWired(packageRoot, results);
|
|
464
|
-
await checkLicenseEntryPointAcrossProviders(packageRoot, results);
|
|
465
|
-
|
|
466
|
-
return results;
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
/**
|
|
470
|
-
* Format results for CLI output.
|
|
471
|
-
*/
|
|
472
|
-
function formatResults(results) {
|
|
473
|
-
const status = results.errors.length === 0 ? 'PASS' : 'FAIL';
|
|
474
|
-
const lines = [
|
|
475
|
-
`Package Validation: ${status} (${results.passed}/${results.checks} checks passed)`,
|
|
476
|
-
];
|
|
477
|
-
|
|
478
|
-
if (results.errors.length > 0) {
|
|
479
|
-
lines.push('');
|
|
480
|
-
lines.push('Errors:');
|
|
481
|
-
results.errors.forEach(e => lines.push(` ✗ ${e}`));
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
if (results.warnings.length > 0) {
|
|
485
|
-
lines.push('');
|
|
486
|
-
lines.push('Warnings:');
|
|
487
|
-
results.warnings.forEach(w => lines.push(` ⚠ ${w}`));
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
return lines.join('\n');
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
// CLI entry point
|
|
494
|
-
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
495
|
-
const packageRoot = join(__dirname, '..');
|
|
496
|
-
validatePackage(packageRoot).then(results => {
|
|
497
|
-
console.log(formatResults(results));
|
|
498
|
-
if (results.errors.length > 0) {
|
|
499
|
-
process.exit(1);
|
|
500
|
-
}
|
|
501
|
-
}).catch(err => {
|
|
502
|
-
console.error('validate-package failed:', err);
|
|
503
|
-
process.exit(1);
|
|
504
|
-
});
|
|
505
|
-
}
|