chati-dev 1.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/assets/logo.txt +6 -0
- package/bin/chati.js +175 -0
- package/framework/agents/build/dev.md +342 -0
- package/framework/agents/clarity/architect.md +263 -0
- package/framework/agents/clarity/brief.md +277 -0
- package/framework/agents/clarity/brownfield-wu.md +288 -0
- package/framework/agents/clarity/detail.md +274 -0
- package/framework/agents/clarity/greenfield-wu.md +231 -0
- package/framework/agents/clarity/phases.md +272 -0
- package/framework/agents/clarity/tasks.md +279 -0
- package/framework/agents/clarity/ux.md +293 -0
- package/framework/agents/deploy/devops.md +321 -0
- package/framework/agents/quality/qa-implementation.md +310 -0
- package/framework/agents/quality/qa-planning.md +289 -0
- package/framework/config.yaml +8 -0
- package/framework/constitution.md +238 -0
- package/framework/frameworks/decision-heuristics.yaml +64 -0
- package/framework/frameworks/quality-dimensions.yaml +59 -0
- package/framework/i18n/en.yaml +78 -0
- package/framework/i18n/es.yaml +78 -0
- package/framework/i18n/fr.yaml +78 -0
- package/framework/i18n/pt.yaml +78 -0
- package/framework/intelligence/confidence.yaml +42 -0
- package/framework/intelligence/gotchas.yaml +51 -0
- package/framework/intelligence/patterns.yaml +32 -0
- package/framework/migrations/v1.0-to-v1.1.yaml +48 -0
- package/framework/orchestrator/chati.md +333 -0
- package/framework/patterns/elicitation.md +137 -0
- package/framework/quality-gates/implementation-gate.md +64 -0
- package/framework/quality-gates/planning-gate.md +52 -0
- package/framework/schemas/config.schema.json +42 -0
- package/framework/schemas/session.schema.json +103 -0
- package/framework/schemas/task.schema.json +71 -0
- package/framework/templates/brownfield-prd-tmpl.yaml +103 -0
- package/framework/templates/fullstack-architecture-tmpl.yaml +101 -0
- package/framework/templates/prd-tmpl.yaml +94 -0
- package/framework/templates/qa-gate-tmpl.yaml +96 -0
- package/framework/templates/task-tmpl.yaml +85 -0
- package/framework/workflows/brownfield-discovery.yaml +75 -0
- package/framework/workflows/brownfield-fullstack.yaml +104 -0
- package/framework/workflows/brownfield-service.yaml +81 -0
- package/framework/workflows/brownfield-ui.yaml +87 -0
- package/framework/workflows/greenfield-fullstack.yaml +108 -0
- package/package.json +60 -0
- package/scripts/bundle-framework.js +58 -0
- package/src/config/ide-configs.js +80 -0
- package/src/config/mcp-configs.js +136 -0
- package/src/dashboard/data-reader.js +99 -0
- package/src/dashboard/layout.js +161 -0
- package/src/dashboard/renderer.js +104 -0
- package/src/installer/core.js +221 -0
- package/src/installer/templates.js +97 -0
- package/src/installer/validator.js +114 -0
- package/src/upgrade/backup.js +107 -0
- package/src/upgrade/checker.js +105 -0
- package/src/upgrade/migrator.js +171 -0
- package/src/utils/colors.js +18 -0
- package/src/utils/detector.js +51 -0
- package/src/utils/logger.js +41 -0
- package/src/wizard/feedback.js +76 -0
- package/src/wizard/i18n.js +168 -0
- package/src/wizard/index.js +107 -0
- package/src/wizard/questions.js +169 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { mkdirSync, writeFileSync, copyFileSync, existsSync, readFileSync } from 'fs';
|
|
2
|
+
import { join, dirname } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import yaml from 'js-yaml';
|
|
5
|
+
import { IDE_CONFIGS } from '../config/ide-configs.js';
|
|
6
|
+
import { generateClaudeMCPConfig } from '../config/mcp-configs.js';
|
|
7
|
+
import { generateSessionYaml, generateConfigYaml, generateClaudeMd } from './templates.js';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
// When published to npm: framework files are bundled at packages/chati-dev/framework/
|
|
12
|
+
// When developing locally in monorepo: fallback to monorepo root chati.dev/
|
|
13
|
+
const BUNDLED_SOURCE = join(__dirname, '..', '..', 'framework');
|
|
14
|
+
const MONOREPO_SOURCE = join(__dirname, '..', '..', '..', '..', 'chati.dev');
|
|
15
|
+
const FRAMEWORK_SOURCE = existsSync(BUNDLED_SOURCE) ? BUNDLED_SOURCE : MONOREPO_SOURCE;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Install chati.dev framework into target directory
|
|
19
|
+
*/
|
|
20
|
+
export async function installFramework(config) {
|
|
21
|
+
const { targetDir, projectType, language, selectedIDEs, selectedMCPs, projectName, version } = config;
|
|
22
|
+
|
|
23
|
+
// 1. Create .chati/ session directory
|
|
24
|
+
createDir(join(targetDir, '.chati'));
|
|
25
|
+
writeFileSync(
|
|
26
|
+
join(targetDir, '.chati', 'session.yaml'),
|
|
27
|
+
generateSessionYaml({ projectName, projectType, language, selectedIDEs, selectedMCPs }),
|
|
28
|
+
'utf-8'
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// 2. Create chati.dev/ framework directory (copy from source)
|
|
32
|
+
const frameworkDir = join(targetDir, 'chati.dev');
|
|
33
|
+
createDir(frameworkDir);
|
|
34
|
+
|
|
35
|
+
// Copy framework structure
|
|
36
|
+
const frameworkDirs = [
|
|
37
|
+
'orchestrator',
|
|
38
|
+
'agents/clarity', 'agents/quality', 'agents/build', 'agents/deploy',
|
|
39
|
+
'templates', 'workflows', 'quality-gates',
|
|
40
|
+
'schemas', 'frameworks', 'intelligence', 'patterns',
|
|
41
|
+
'i18n', 'migrations',
|
|
42
|
+
'artifacts/0-WU', 'artifacts/1-Brief', 'artifacts/2-PRD',
|
|
43
|
+
'artifacts/3-Architecture', 'artifacts/4-UX', 'artifacts/5-Phases',
|
|
44
|
+
'artifacts/6-Tasks', 'artifacts/7-QA-Planning', 'artifacts/8-Validation',
|
|
45
|
+
'artifacts/handoffs', 'artifacts/decisions',
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
for (const dir of frameworkDirs) {
|
|
49
|
+
createDir(join(frameworkDir, dir));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Copy framework files from source
|
|
53
|
+
copyFrameworkFiles(frameworkDir);
|
|
54
|
+
|
|
55
|
+
// Write config.yaml
|
|
56
|
+
writeFileSync(
|
|
57
|
+
join(frameworkDir, 'config.yaml'),
|
|
58
|
+
generateConfigYaml({ version, projectType, language, selectedIDEs }),
|
|
59
|
+
'utf-8'
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
// 3. Configure IDEs
|
|
63
|
+
for (const ideKey of selectedIDEs) {
|
|
64
|
+
await configureIDE(targetDir, ideKey, selectedMCPs);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 4. Create/update CLAUDE.md at root
|
|
68
|
+
writeFileSync(
|
|
69
|
+
join(targetDir, 'CLAUDE.md'),
|
|
70
|
+
generateClaudeMd({ projectName, projectType, language }),
|
|
71
|
+
'utf-8'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Copy framework files from the chati.dev source directory
|
|
77
|
+
*/
|
|
78
|
+
function copyFrameworkFiles(destDir) {
|
|
79
|
+
if (!existsSync(FRAMEWORK_SOURCE)) return;
|
|
80
|
+
|
|
81
|
+
const filesToCopy = [
|
|
82
|
+
'constitution.md',
|
|
83
|
+
'orchestrator/chati.md',
|
|
84
|
+
// CLARITY agents
|
|
85
|
+
'agents/clarity/greenfield-wu.md',
|
|
86
|
+
'agents/clarity/brownfield-wu.md',
|
|
87
|
+
'agents/clarity/brief.md',
|
|
88
|
+
'agents/clarity/detail.md',
|
|
89
|
+
'agents/clarity/architect.md',
|
|
90
|
+
'agents/clarity/ux.md',
|
|
91
|
+
'agents/clarity/phases.md',
|
|
92
|
+
'agents/clarity/tasks.md',
|
|
93
|
+
// Quality agents
|
|
94
|
+
'agents/quality/qa-planning.md',
|
|
95
|
+
'agents/quality/qa-implementation.md',
|
|
96
|
+
// BUILD + DEPLOY agents
|
|
97
|
+
'agents/build/dev.md',
|
|
98
|
+
'agents/deploy/devops.md',
|
|
99
|
+
// Templates
|
|
100
|
+
'templates/prd-tmpl.yaml',
|
|
101
|
+
'templates/brownfield-prd-tmpl.yaml',
|
|
102
|
+
'templates/fullstack-architecture-tmpl.yaml',
|
|
103
|
+
'templates/task-tmpl.yaml',
|
|
104
|
+
'templates/qa-gate-tmpl.yaml',
|
|
105
|
+
// Workflows
|
|
106
|
+
'workflows/greenfield-fullstack.yaml',
|
|
107
|
+
'workflows/brownfield-fullstack.yaml',
|
|
108
|
+
'workflows/brownfield-discovery.yaml',
|
|
109
|
+
'workflows/brownfield-service.yaml',
|
|
110
|
+
'workflows/brownfield-ui.yaml',
|
|
111
|
+
// Quality gates
|
|
112
|
+
'quality-gates/planning-gate.md',
|
|
113
|
+
'quality-gates/implementation-gate.md',
|
|
114
|
+
// Schemas
|
|
115
|
+
'schemas/session.schema.json',
|
|
116
|
+
'schemas/config.schema.json',
|
|
117
|
+
'schemas/task.schema.json',
|
|
118
|
+
// Frameworks
|
|
119
|
+
'frameworks/quality-dimensions.yaml',
|
|
120
|
+
'frameworks/decision-heuristics.yaml',
|
|
121
|
+
// Intelligence
|
|
122
|
+
'intelligence/gotchas.yaml',
|
|
123
|
+
'intelligence/patterns.yaml',
|
|
124
|
+
'intelligence/confidence.yaml',
|
|
125
|
+
// Patterns
|
|
126
|
+
'patterns/elicitation.md',
|
|
127
|
+
// i18n
|
|
128
|
+
'i18n/en.yaml',
|
|
129
|
+
'i18n/pt.yaml',
|
|
130
|
+
'i18n/es.yaml',
|
|
131
|
+
'i18n/fr.yaml',
|
|
132
|
+
// Migrations
|
|
133
|
+
'migrations/v1.0-to-v1.1.yaml',
|
|
134
|
+
];
|
|
135
|
+
|
|
136
|
+
for (const file of filesToCopy) {
|
|
137
|
+
const src = join(FRAMEWORK_SOURCE, file);
|
|
138
|
+
const dest = join(destDir, file);
|
|
139
|
+
|
|
140
|
+
if (existsSync(src)) {
|
|
141
|
+
createDir(dirname(dest));
|
|
142
|
+
copyFileSync(src, dest);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Configure a specific IDE
|
|
149
|
+
*/
|
|
150
|
+
async function configureIDE(targetDir, ideKey, selectedMCPs) {
|
|
151
|
+
const config = IDE_CONFIGS[ideKey];
|
|
152
|
+
if (!config) return;
|
|
153
|
+
|
|
154
|
+
// Create config directory
|
|
155
|
+
createDir(join(targetDir, config.configPath));
|
|
156
|
+
|
|
157
|
+
if (ideKey === 'claude-code') {
|
|
158
|
+
// Thin router
|
|
159
|
+
const routerContent = `---
|
|
160
|
+
# chati.dev Thin Router
|
|
161
|
+
# This file delegates to the full orchestrator at chati.dev/orchestrator/chati.md
|
|
162
|
+
# DO NOT add logic here -- pure delegation only
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
Read and execute the orchestrator at \`chati.dev/orchestrator/chati.md\`.
|
|
166
|
+
|
|
167
|
+
Pass through all context: session (.chati/session.yaml), handoffs (chati.dev/artifacts/handoffs/),
|
|
168
|
+
artifacts (chati.dev/artifacts/), constitution (chati.dev/constitution.md).
|
|
169
|
+
|
|
170
|
+
This is a thin router. All logic lives in the orchestrator.
|
|
171
|
+
`;
|
|
172
|
+
writeFileSync(join(targetDir, '.claude', 'commands', 'chati.md'), routerContent, 'utf-8');
|
|
173
|
+
|
|
174
|
+
// MCP config
|
|
175
|
+
if (selectedMCPs.length > 0) {
|
|
176
|
+
const mcpConfig = generateClaudeMCPConfig(selectedMCPs);
|
|
177
|
+
writeFileSync(
|
|
178
|
+
join(targetDir, '.claude', 'mcp.json'),
|
|
179
|
+
JSON.stringify(mcpConfig, null, 2) + '\n',
|
|
180
|
+
'utf-8'
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
// For other IDEs, create a rules file pointing to chati.dev/
|
|
185
|
+
const rulesContent = `# chati.dev Framework Rules
|
|
186
|
+
# This file configures ${config.name} to work with chati.dev
|
|
187
|
+
|
|
188
|
+
## Framework Location
|
|
189
|
+
All framework content is in the \`chati.dev/\` directory.
|
|
190
|
+
|
|
191
|
+
## Session State
|
|
192
|
+
Runtime session state is in \`.chati/session.yaml\` (IDE-agnostic).
|
|
193
|
+
|
|
194
|
+
## Getting Started
|
|
195
|
+
The orchestrator is at \`chati.dev/orchestrator/chati.md\`.
|
|
196
|
+
Read it to understand routing, session management, and agent activation.
|
|
197
|
+
|
|
198
|
+
## Constitution
|
|
199
|
+
Governance rules are in \`chati.dev/constitution.md\` (10 Articles).
|
|
200
|
+
|
|
201
|
+
## Agents
|
|
202
|
+
- CLARITY: chati.dev/agents/clarity/ (8 agents)
|
|
203
|
+
- Quality: chati.dev/agents/quality/ (2 agents)
|
|
204
|
+
- BUILD: chati.dev/agents/build/ (1 agent)
|
|
205
|
+
- DEPLOY: chati.dev/agents/deploy/ (1 agent)
|
|
206
|
+
`;
|
|
207
|
+
if (config.rulesFile) {
|
|
208
|
+
createDir(dirname(join(targetDir, config.rulesFile)));
|
|
209
|
+
writeFileSync(join(targetDir, config.rulesFile), rulesContent, 'utf-8');
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Recursively create directory if it doesn't exist
|
|
216
|
+
*/
|
|
217
|
+
function createDir(dir) {
|
|
218
|
+
if (!existsSync(dir)) {
|
|
219
|
+
mkdirSync(dir, { recursive: true });
|
|
220
|
+
}
|
|
221
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import yaml from 'js-yaml';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generate session.yaml content
|
|
5
|
+
*/
|
|
6
|
+
export function generateSessionYaml(config) {
|
|
7
|
+
const { projectName, projectType, language, selectedIDEs, selectedMCPs } = config;
|
|
8
|
+
|
|
9
|
+
const session = {
|
|
10
|
+
project: {
|
|
11
|
+
name: projectName,
|
|
12
|
+
type: projectType,
|
|
13
|
+
state: 'clarity',
|
|
14
|
+
},
|
|
15
|
+
execution_mode: 'interactive',
|
|
16
|
+
current_agent: '',
|
|
17
|
+
language: language,
|
|
18
|
+
ides: selectedIDEs,
|
|
19
|
+
mcps: selectedMCPs,
|
|
20
|
+
user_level: 'auto',
|
|
21
|
+
user_level_confidence: 0.0,
|
|
22
|
+
agents: {},
|
|
23
|
+
backlog: [],
|
|
24
|
+
last_handoff: '',
|
|
25
|
+
deviations: [],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Initialize all 12 agent statuses
|
|
29
|
+
const agentNames = [
|
|
30
|
+
'greenfield-wu', 'brownfield-wu', 'brief', 'detail',
|
|
31
|
+
'architect', 'ux', 'phases', 'tasks',
|
|
32
|
+
'qa-planning', 'dev', 'qa-implementation', 'devops',
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
for (const agent of agentNames) {
|
|
36
|
+
session.agents[agent] = {
|
|
37
|
+
status: 'pending',
|
|
38
|
+
score: 0,
|
|
39
|
+
criteria_count: 0,
|
|
40
|
+
completed_at: null,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return yaml.dump(session, { lineWidth: -1, quotingType: '"', forceQuotes: false });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Generate config.yaml content
|
|
49
|
+
*/
|
|
50
|
+
export function generateConfigYaml(config) {
|
|
51
|
+
const { version, projectType, language, selectedIDEs } = config;
|
|
52
|
+
|
|
53
|
+
const configData = {
|
|
54
|
+
version: version,
|
|
55
|
+
installed_at: new Date().toISOString(),
|
|
56
|
+
updated_at: new Date().toISOString(),
|
|
57
|
+
installer_version: version,
|
|
58
|
+
project_type: projectType,
|
|
59
|
+
language: language,
|
|
60
|
+
ides: selectedIDEs,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return yaml.dump(configData, { lineWidth: -1, quotingType: '"', forceQuotes: false });
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Generate CLAUDE.md content
|
|
68
|
+
*/
|
|
69
|
+
export function generateClaudeMd(config) {
|
|
70
|
+
const { projectName, projectType, language } = config;
|
|
71
|
+
|
|
72
|
+
return `# ${projectName}
|
|
73
|
+
|
|
74
|
+
## Project Context
|
|
75
|
+
- **Type**: ${projectType === 'greenfield' ? 'Greenfield (new project)' : 'Brownfield (existing project)'}
|
|
76
|
+
- **State**: CLARITY (planning phase)
|
|
77
|
+
- **Language**: ${language}
|
|
78
|
+
- **Current Agent**: None (ready to start)
|
|
79
|
+
|
|
80
|
+
## Quick Start
|
|
81
|
+
Type \`/chati\` to activate the orchestrator. It will guide you through the entire process.
|
|
82
|
+
|
|
83
|
+
## Key Files
|
|
84
|
+
- **Session**: \`.chati/session.yaml\` (runtime state)
|
|
85
|
+
- **Constitution**: \`chati.dev/constitution.md\` (governance)
|
|
86
|
+
- **Orchestrator**: \`chati.dev/orchestrator/chati.md\` (entry point)
|
|
87
|
+
|
|
88
|
+
## Pipeline
|
|
89
|
+
CLARITY (planning) -> BUILD (implementation) -> VALIDATE -> DEPLOY
|
|
90
|
+
|
|
91
|
+
## Recent Decisions
|
|
92
|
+
_No decisions yet. Start with /chati._
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
_Auto-updated by chati.dev agents (Protocol 5.4)_
|
|
96
|
+
`;
|
|
97
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Validate chati.dev installation
|
|
6
|
+
* Checks all 13 agents, constitution, session, schemas, etc.
|
|
7
|
+
*/
|
|
8
|
+
export async function validateInstallation(targetDir) {
|
|
9
|
+
const results = {
|
|
10
|
+
agents: { pass: false, details: [] },
|
|
11
|
+
constitution: { pass: false, details: [] },
|
|
12
|
+
session: { pass: false, details: [] },
|
|
13
|
+
schemas: { pass: false, details: [] },
|
|
14
|
+
workflows: { pass: false, details: [] },
|
|
15
|
+
templates: { pass: false, details: [] },
|
|
16
|
+
total: 0,
|
|
17
|
+
passed: 0,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Check all 13 agents (orchestrator + 12 specialized)
|
|
21
|
+
const agentFiles = [
|
|
22
|
+
'orchestrator/chati.md',
|
|
23
|
+
'agents/clarity/greenfield-wu.md',
|
|
24
|
+
'agents/clarity/brownfield-wu.md',
|
|
25
|
+
'agents/clarity/brief.md',
|
|
26
|
+
'agents/clarity/detail.md',
|
|
27
|
+
'agents/clarity/architect.md',
|
|
28
|
+
'agents/clarity/ux.md',
|
|
29
|
+
'agents/clarity/phases.md',
|
|
30
|
+
'agents/clarity/tasks.md',
|
|
31
|
+
'agents/quality/qa-planning.md',
|
|
32
|
+
'agents/quality/qa-implementation.md',
|
|
33
|
+
'agents/build/dev.md',
|
|
34
|
+
'agents/deploy/devops.md',
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
let agentCount = 0;
|
|
38
|
+
for (const file of agentFiles) {
|
|
39
|
+
const filePath = join(targetDir, 'chati.dev', file);
|
|
40
|
+
if (existsSync(filePath)) {
|
|
41
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
42
|
+
// Check that agent implements protocols (look for key protocol references)
|
|
43
|
+
const hasProtocols = content.includes('Protocol') || content.includes('protocol');
|
|
44
|
+
agentCount++;
|
|
45
|
+
results.agents.details.push({ file, exists: true, hasProtocols });
|
|
46
|
+
} else {
|
|
47
|
+
results.agents.details.push({ file, exists: false, hasProtocols: false });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
results.agents.pass = agentCount === 13;
|
|
51
|
+
results.total += 1;
|
|
52
|
+
if (results.agents.pass) results.passed += 1;
|
|
53
|
+
|
|
54
|
+
// Check constitution
|
|
55
|
+
const constitutionPath = join(targetDir, 'chati.dev', 'constitution.md');
|
|
56
|
+
if (existsSync(constitutionPath)) {
|
|
57
|
+
const content = readFileSync(constitutionPath, 'utf-8');
|
|
58
|
+
const articleCount = (content.match(/^## Article/gm) || []).length;
|
|
59
|
+
results.constitution.pass = articleCount >= 10;
|
|
60
|
+
results.constitution.details.push({ articleCount });
|
|
61
|
+
}
|
|
62
|
+
results.total += 1;
|
|
63
|
+
if (results.constitution.pass) results.passed += 1;
|
|
64
|
+
|
|
65
|
+
// Check session.yaml
|
|
66
|
+
const sessionPath = join(targetDir, '.chati', 'session.yaml');
|
|
67
|
+
results.session.pass = existsSync(sessionPath);
|
|
68
|
+
results.total += 1;
|
|
69
|
+
if (results.session.pass) results.passed += 1;
|
|
70
|
+
|
|
71
|
+
// Check schemas
|
|
72
|
+
const schemaFiles = ['session.schema.json', 'config.schema.json', 'task.schema.json'];
|
|
73
|
+
let schemaCount = 0;
|
|
74
|
+
for (const file of schemaFiles) {
|
|
75
|
+
if (existsSync(join(targetDir, 'chati.dev', 'schemas', file))) {
|
|
76
|
+
schemaCount++;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
results.schemas.pass = schemaCount === 3;
|
|
80
|
+
results.total += 1;
|
|
81
|
+
if (results.schemas.pass) results.passed += 1;
|
|
82
|
+
|
|
83
|
+
// Check workflows
|
|
84
|
+
const workflowFiles = [
|
|
85
|
+
'greenfield-fullstack.yaml', 'brownfield-fullstack.yaml',
|
|
86
|
+
'brownfield-discovery.yaml', 'brownfield-service.yaml', 'brownfield-ui.yaml',
|
|
87
|
+
];
|
|
88
|
+
let workflowCount = 0;
|
|
89
|
+
for (const file of workflowFiles) {
|
|
90
|
+
if (existsSync(join(targetDir, 'chati.dev', 'workflows', file))) {
|
|
91
|
+
workflowCount++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
results.workflows.pass = workflowCount === 5;
|
|
95
|
+
results.total += 1;
|
|
96
|
+
if (results.workflows.pass) results.passed += 1;
|
|
97
|
+
|
|
98
|
+
// Check templates
|
|
99
|
+
const templateFiles = [
|
|
100
|
+
'prd-tmpl.yaml', 'brownfield-prd-tmpl.yaml',
|
|
101
|
+
'fullstack-architecture-tmpl.yaml', 'task-tmpl.yaml', 'qa-gate-tmpl.yaml',
|
|
102
|
+
];
|
|
103
|
+
let templateCount = 0;
|
|
104
|
+
for (const file of templateFiles) {
|
|
105
|
+
if (existsSync(join(targetDir, 'chati.dev', 'templates', file))) {
|
|
106
|
+
templateCount++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
results.templates.pass = templateCount === 5;
|
|
110
|
+
results.total += 1;
|
|
111
|
+
if (results.templates.pass) results.passed += 1;
|
|
112
|
+
|
|
113
|
+
return results;
|
|
114
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, cpSync, rmSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Create backup of chati.dev/ directory before upgrade
|
|
6
|
+
* Backup location: chati.dev/.backup-v{version}/
|
|
7
|
+
*/
|
|
8
|
+
export function createBackup(targetDir, currentVersion) {
|
|
9
|
+
const frameworkDir = join(targetDir, 'chati.dev');
|
|
10
|
+
const backupDir = join(frameworkDir, `.backup-v${currentVersion}`);
|
|
11
|
+
|
|
12
|
+
if (existsSync(backupDir)) {
|
|
13
|
+
// Remove old backup of same version
|
|
14
|
+
rmSync(backupDir, { recursive: true, force: true });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
mkdirSync(backupDir, { recursive: true });
|
|
18
|
+
|
|
19
|
+
// Backup framework files (NOT artifacts or intelligence)
|
|
20
|
+
const dirsToBackup = [
|
|
21
|
+
'orchestrator',
|
|
22
|
+
'agents',
|
|
23
|
+
'templates',
|
|
24
|
+
'workflows',
|
|
25
|
+
'quality-gates',
|
|
26
|
+
'schemas',
|
|
27
|
+
'frameworks',
|
|
28
|
+
'i18n',
|
|
29
|
+
'patterns',
|
|
30
|
+
'migrations',
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const filesToBackup = [
|
|
34
|
+
'constitution.md',
|
|
35
|
+
'config.yaml',
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
for (const dir of dirsToBackup) {
|
|
39
|
+
const src = join(frameworkDir, dir);
|
|
40
|
+
const dest = join(backupDir, dir);
|
|
41
|
+
if (existsSync(src)) {
|
|
42
|
+
cpSync(src, dest, { recursive: true });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const file of filesToBackup) {
|
|
47
|
+
const src = join(frameworkDir, file);
|
|
48
|
+
const dest = join(backupDir, file);
|
|
49
|
+
if (existsSync(src)) {
|
|
50
|
+
cpSync(src, dest);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return backupDir;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Restore from backup (rollback)
|
|
59
|
+
*/
|
|
60
|
+
export function restoreFromBackup(targetDir, version) {
|
|
61
|
+
const frameworkDir = join(targetDir, 'chati.dev');
|
|
62
|
+
const backupDir = join(frameworkDir, `.backup-v${version}`);
|
|
63
|
+
|
|
64
|
+
if (!existsSync(backupDir)) {
|
|
65
|
+
throw new Error(`Backup not found: ${backupDir}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Restore backed-up directories
|
|
69
|
+
const dirsToRestore = [
|
|
70
|
+
'orchestrator',
|
|
71
|
+
'agents',
|
|
72
|
+
'templates',
|
|
73
|
+
'workflows',
|
|
74
|
+
'quality-gates',
|
|
75
|
+
'schemas',
|
|
76
|
+
'frameworks',
|
|
77
|
+
'i18n',
|
|
78
|
+
'patterns',
|
|
79
|
+
'migrations',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
const filesToRestore = [
|
|
83
|
+
'constitution.md',
|
|
84
|
+
'config.yaml',
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
for (const dir of dirsToRestore) {
|
|
88
|
+
const src = join(backupDir, dir);
|
|
89
|
+
const dest = join(frameworkDir, dir);
|
|
90
|
+
if (existsSync(src)) {
|
|
91
|
+
if (existsSync(dest)) {
|
|
92
|
+
rmSync(dest, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
cpSync(src, dest, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const file of filesToRestore) {
|
|
99
|
+
const src = join(backupDir, file);
|
|
100
|
+
const dest = join(frameworkDir, file);
|
|
101
|
+
if (existsSync(src)) {
|
|
102
|
+
cpSync(src, dest);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Read current installed version from config.yaml
|
|
8
|
+
*/
|
|
9
|
+
export function getCurrentVersion(targetDir) {
|
|
10
|
+
const configPath = join(targetDir, 'chati.dev', 'config.yaml');
|
|
11
|
+
if (!existsSync(configPath)) return null;
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const config = yaml.load(readFileSync(configPath, 'utf-8'));
|
|
15
|
+
return config?.version || null;
|
|
16
|
+
} catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Check for available updates
|
|
23
|
+
* In v1, this compares against the CLI package version
|
|
24
|
+
* Future versions will query npm registry or GitHub releases
|
|
25
|
+
*/
|
|
26
|
+
export async function checkForUpdate(targetDir, cliVersion) {
|
|
27
|
+
const currentVersion = getCurrentVersion(targetDir);
|
|
28
|
+
|
|
29
|
+
if (!currentVersion) {
|
|
30
|
+
return {
|
|
31
|
+
hasUpdate: false,
|
|
32
|
+
error: 'Could not read current version from chati.dev/config.yaml',
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!semver.valid(currentVersion)) {
|
|
37
|
+
return {
|
|
38
|
+
hasUpdate: false,
|
|
39
|
+
error: `Invalid version in config.yaml: ${currentVersion}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const latestVersion = cliVersion; // In v1, latest = CLI version
|
|
44
|
+
|
|
45
|
+
if (semver.gt(latestVersion, currentVersion)) {
|
|
46
|
+
return {
|
|
47
|
+
hasUpdate: true,
|
|
48
|
+
currentVersion,
|
|
49
|
+
latestVersion,
|
|
50
|
+
changes: getChangelog(currentVersion, latestVersion),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
hasUpdate: false,
|
|
56
|
+
currentVersion,
|
|
57
|
+
latestVersion,
|
|
58
|
+
message: 'You are on the latest version.',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get changelog between versions
|
|
64
|
+
* In v1, returns placeholder. Future: read from CHANGELOG.md or API
|
|
65
|
+
*/
|
|
66
|
+
function getChangelog(fromVersion, toVersion) {
|
|
67
|
+
return [
|
|
68
|
+
`Changes from v${fromVersion} to v${toVersion}:`,
|
|
69
|
+
' Check release notes for details.',
|
|
70
|
+
];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Update config.yaml with the new version after successful upgrade
|
|
75
|
+
*/
|
|
76
|
+
export function updateConfigVersion(targetDir, newVersion) {
|
|
77
|
+
const configPath = join(targetDir, 'chati.dev', 'config.yaml');
|
|
78
|
+
if (!existsSync(configPath)) return false;
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const config = yaml.load(readFileSync(configPath, 'utf-8'));
|
|
82
|
+
config.version = newVersion;
|
|
83
|
+
writeFileSync(configPath, yaml.dump(config, { lineWidth: -1 }), 'utf-8');
|
|
84
|
+
return true;
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Compare two versions
|
|
92
|
+
*/
|
|
93
|
+
export function compareVersions(current, target) {
|
|
94
|
+
if (!semver.valid(current) || !semver.valid(target)) {
|
|
95
|
+
return { valid: false, error: 'Invalid version format' };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return {
|
|
99
|
+
valid: true,
|
|
100
|
+
isUpgrade: semver.gt(target, current),
|
|
101
|
+
isDowngrade: semver.lt(target, current),
|
|
102
|
+
isSame: semver.eq(target, current),
|
|
103
|
+
diff: semver.diff(target, current), // 'major', 'minor', 'patch'
|
|
104
|
+
};
|
|
105
|
+
}
|